Skip to content

Commit 61ccfc4

Browse files
jabrenacursoragent
andauthored
Create XSD and update validation processes (#118)
* Migrate from DTD to XSD validation with namespace support Co-authored-by: bren <bren@juanantonio.info> * Checkpoint before follow-up message * Switch from XML Schema (XSD) to DTD validation Co-authored-by: bren <bren@juanantonio.info> * Checkpoint before follow-up message * Checkpoint before follow-up message * Checkpoint before follow-up message * Migrate from DTD to XSD validation with improved error handling Co-authored-by: bren <bren@juanantonio.info> * Update Java version from 21 to 24 in project configuration Co-authored-by: bren <bren@juanantonio.info> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent 0387543 commit 61ccfc4

6 files changed

Lines changed: 948 additions & 35 deletions

File tree

spml/pom.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@
184184
</configuration>
185185
</plugin>
186186

187-
<!-- XML validation against DTD -->
187+
<!-- XML validation against XSD -->
188188
<plugin>
189189
<groupId>org.codehaus.mojo</groupId>
190190
<artifactId>xml-maven-plugin</artifactId>
@@ -203,6 +203,7 @@
203203
<include>*.xml</include>
204204
</includes>
205205
<validating>true</validating>
206+
<schemaLanguage>http://www.w3.org/2001/XMLSchema</schemaLanguage>
206207
</validationSet>
207208
</validationSets>
208209
</configuration>

spml/src/main/java/info/jab/xml/CursorRuleGenerator.java

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@
44
import java.io.StringWriter;
55
import java.util.Objects;
66
import java.util.Optional;
7+
import javax.xml.XMLConstants;
78
import javax.xml.parsers.ParserConfigurationException;
89
import javax.xml.parsers.SAXParserFactory;
910
import javax.xml.transform.*;
1011
import javax.xml.transform.sax.SAXSource;
1112
import javax.xml.transform.stream.StreamResult;
1213
import javax.xml.transform.stream.StreamSource;
13-
import org.xml.sax.EntityResolver;
14+
import javax.xml.validation.Schema;
15+
import javax.xml.validation.SchemaFactory;
16+
import org.xml.sax.ErrorHandler;
1417
import org.xml.sax.InputSource;
1518
import org.xml.sax.SAXException;
19+
import org.xml.sax.SAXParseException;
1620
import org.xml.sax.XMLReader;
1721

1822
/**
@@ -21,7 +25,7 @@
2125
*/
2226
public final class CursorRuleGenerator {
2327

24-
private static final String DTD_FILE_NAME = "system-prompt.dtd";
28+
private static final String XSD_FILE_NAME = "system-prompt.xsd";
2529

2630
// ===============================================================
2731
// PUBLIC API - Entry point for cursor rule generation
@@ -64,17 +68,28 @@ private Optional<InputStream> loadResource(String fileName) {
6468
}
6569

6670
/**
67-
* Step 2: Creates SAXSource with custom EntityResolver.
68-
* Pure function that creates immutable SAXSource using modern SAX API.
71+
* Step 2: Creates SAXSource with XSD validation.
72+
* Pure function that creates immutable SAXSource with schema validation.
6973
*/
7074
private SAXSource createSaxSource(TransformationSources sources) {
7175
try {
72-
// Modern approach: Use SAXParserFactory instead of deprecated XMLReaderFactory
73-
XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
74-
xmlReader.setEntityResolver(new ResourceEntityResolver());
76+
// Create SAX parser factory with namespace awareness and validation
77+
SAXParserFactory factory = SAXParserFactory.newInstance();
78+
factory.setNamespaceAware(true);
79+
factory.setValidating(false); // We'll use schema validation instead
80+
81+
// Load XSD schema
82+
Optional<Schema> schema = loadXsdSchema();
83+
if (schema.isPresent()) {
84+
factory.setSchema(schema.get());
85+
}
86+
87+
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
88+
xmlReader.setErrorHandler(new ValidationErrorHandler());
89+
7590
return new SAXSource(xmlReader, new InputSource(sources.xmlStream()));
7691
} catch (SAXException | ParserConfigurationException e) {
77-
throw new RuntimeException("Failed to create SAX source with modern XMLReader API", e);
92+
throw new RuntimeException("Failed to create SAX source with XSD validation", e);
7893
}
7994
}
8095

@@ -125,27 +140,40 @@ private record TransformationSources(InputStream xmlStream, InputStream xslStrea
125140
}
126141

127142
/**
128-
* Custom EntityResolver as functional interface implementation.
129-
* Used by createSaxSource to resolve DTD references.
143+
* Loads XSD schema from classpath for validation.
144+
* Returns Optional to handle missing schema gracefully.
130145
*/
131-
private static final class ResourceEntityResolver implements EntityResolver {
146+
private Optional<Schema> loadXsdSchema() {
147+
return loadResource(XSD_FILE_NAME)
148+
.map(xsdStream -> {
149+
try {
150+
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
151+
return schemaFactory.newSchema(new StreamSource(xsdStream));
152+
} catch (SAXException e) {
153+
throw new RuntimeException("Failed to load XSD schema: " + XSD_FILE_NAME, e);
154+
}
155+
});
156+
}
157+
158+
/**
159+
* Custom ErrorHandler for XSD validation errors.
160+
* Provides better error reporting for validation issues.
161+
*/
162+
private static final class ValidationErrorHandler implements ErrorHandler {
132163
@Override
133-
public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
134-
// Only handle system IDs we can resolve; return null for default SAX behavior otherwise
135-
return Optional.ofNullable(systemId)
136-
.filter(id -> id.endsWith(DTD_FILE_NAME))
137-
.flatMap(this::loadDtdFromClasspath)
138-
.orElse(null); // SAX contract: null means "use default resolution"
164+
public void warning(SAXParseException exception) throws SAXException {
165+
// Log warning in a real application
166+
System.err.println("XSD Validation Warning: " + exception.getMessage());
139167
}
140168

141-
private Optional<InputSource> loadDtdFromClasspath(String systemId) {
142-
return Optional.ofNullable(
143-
getClass().getClassLoader().getResourceAsStream(DTD_FILE_NAME)
144-
).map(dtdStream -> {
145-
InputSource inputSource = new InputSource(dtdStream);
146-
inputSource.setSystemId(systemId);
147-
return inputSource;
148-
});
169+
@Override
170+
public void error(SAXParseException exception) throws SAXException {
171+
throw new SAXException("XSD Validation Error: " + exception.getMessage(), exception);
172+
}
173+
174+
@Override
175+
public void fatalError(SAXParseException exception) throws SAXException {
176+
throw new SAXException("XSD Validation Fatal Error: " + exception.getMessage(), exception);
149177
}
150178
}
151179
}

spml/src/main/resources/100-java-checklist-guide.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<!DOCTYPE system-prompt SYSTEM "system-prompt.dtd">
3-
4-
<system-prompt id="100-java-checklist-guide" version="1.0">
2+
<system-prompt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="system-prompt.xsd"
4+
id="100-java-checklist-guide" version="1.0">
55
<metadata>
66
<description></description>
77
<globs></globs>

spml/src/main/resources/110-java-maven-best-practices.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<!DOCTYPE system-prompt SYSTEM "system-prompt.dtd">
3-
4-
<system-prompt id="110-java-maven-best-practices" version="1.0">
2+
<system-prompt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="system-prompt.xsd"
4+
id="110-java-maven-best-practices" version="1.0">
55
<metadata>
66
<description>Maven Best Practices</description>
77
<globs>pom.xml</globs>

spml/src/main/resources/112-java-maven-documentation.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<!DOCTYPE system-prompt SYSTEM "system-prompt.dtd">
3-
4-
<system-prompt id="112-java-maven-documentation" version="1.0">
2+
<system-prompt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="system-prompt.xsd"
4+
id="112-java-maven-documentation" version="1.0">
55
<metadata>
66
<description>Create README-DEV.md with information about how to use the Maven project</description>
77
<globs>pom.xml</globs>

0 commit comments

Comments
 (0)