Skip to content

Commit 59e2bbc

Browse files
committed
Adding a new rule
1 parent f212f0e commit 59e2bbc

6 files changed

Lines changed: 446 additions & 81 deletions

File tree

spml/pom.xml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
<maven-plugin-versions.version>2.18.0</maven-plugin-versions.version>
2222
<extra-enforcer-rules.version>1.10.0</extra-enforcer-rules.version>
2323

24+
<!-- Logging dependency versions -->
25+
<slf4j.version>2.0.15</slf4j.version>
26+
<logback.version>1.5.12</logback.version>
27+
2428
<!-- Test dependency versions -->
2529
<assertj.version>3.26.3</assertj.version>
2630
</properties>
@@ -38,6 +42,19 @@
3842
</dependencyManagement>
3943

4044
<dependencies>
45+
<!-- Logging dependencies -->
46+
<dependency>
47+
<groupId>org.slf4j</groupId>
48+
<artifactId>slf4j-api</artifactId>
49+
<version>${slf4j.version}</version>
50+
</dependency>
51+
<dependency>
52+
<groupId>ch.qos.logback</groupId>
53+
<artifactId>logback-classic</artifactId>
54+
<version>${logback.version}</version>
55+
</dependency>
56+
57+
<!-- Test dependencies -->
4158
<dependency>
4259
<groupId>org.junit.jupiter</groupId>
4360
<artifactId>junit-jupiter-api</artifactId>

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

Lines changed: 117 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,69 +3,149 @@
33
import java.io.InputStream;
44
import java.io.StringWriter;
55
import java.util.Objects;
6+
import java.util.Optional;
7+
import javax.xml.parsers.ParserConfigurationException;
8+
import javax.xml.parsers.SAXParserFactory;
69
import javax.xml.transform.*;
710
import javax.xml.transform.sax.SAXSource;
811
import javax.xml.transform.stream.StreamResult;
912
import javax.xml.transform.stream.StreamSource;
1013
import org.xml.sax.EntityResolver;
1114
import org.xml.sax.InputSource;
15+
import org.xml.sax.SAXException;
1216
import org.xml.sax.XMLReader;
13-
import org.xml.sax.helpers.XMLReaderFactory;
1417

15-
public class CursorRuleGenerator {
18+
/**
19+
* Generator for Cursor Rules using XML/XSLT transformation.
20+
* Follows functional programming principles with immutability and pure functions.
21+
*/
22+
public final class CursorRuleGenerator {
1623

17-
// Custom EntityResolver to resolve DTD from classpath resources
18-
private static class ResourceEntityResolver implements EntityResolver {
19-
@Override
20-
public InputSource resolveEntity(String publicId, String systemId) {
21-
if (systemId != null && systemId.endsWith("system-prompt.dtd")) {
22-
InputStream dtdStream = getClass().getClassLoader().getResourceAsStream("system-prompt.dtd");
23-
if (dtdStream != null) {
24-
InputSource inputSource = new InputSource(dtdStream);
25-
inputSource.setSystemId(systemId);
26-
return inputSource;
27-
}
28-
}
29-
//TODO Not return null
30-
return null; // Use default behavior for other entities
31-
}
32-
}
24+
private static final String DTD_FILE_NAME = "system-prompt.dtd";
3325

26+
// ===============================================================
27+
// PUBLIC API - Entry point for cursor rule generation
28+
// ===============================================================
29+
30+
/**
31+
* Generates cursor rules by transforming XML with XSLT.
32+
* Pure function that depends only on input parameters.
33+
*/
3434
public String generate(String xmlFileName, String xslFileName) {
35-
try {
36-
// Load XML and XSLT from resources
37-
InputStream xmlStream = getClass().getClassLoader().getResourceAsStream(xmlFileName);
38-
InputStream xslStream = getClass().getClassLoader().getResourceAsStream(xslFileName);
35+
return loadTransformationSources(xmlFileName, xslFileName)
36+
.map(this::createSaxSource)
37+
.flatMap(saxSource -> performTransformation(saxSource, xslFileName))
38+
.orElseThrow(() -> new RuntimeException(
39+
"Failed to generate cursor rules for: " + xmlFileName + ", " + xslFileName));
40+
}
3941

40-
if (Objects.isNull(xmlStream) || Objects.isNull(xslStream)) {
41-
throw new RuntimeException("Could not load XML or XSLT resources: " + xmlFileName + ", " + xslFileName);
42-
}
42+
// ===============================================================
43+
// PRIVATE METHODS - Organized in call order for readability
44+
// ===============================================================
45+
46+
/**
47+
* Step 1: Loads XML and XSLT resources as a TransformationSources record.
48+
* Returns Optional to handle missing resources gracefully.
49+
*/
50+
private Optional<TransformationSources> loadTransformationSources(String xmlFileName, String xslFileName) {
51+
return loadResource(xmlFileName)
52+
.flatMap(xmlStream -> loadResource(xslFileName)
53+
.map(xslStream -> new TransformationSources(xmlStream, xslStream)));
54+
}
4355

44-
//TODO not use deprecated methods
45-
// Create XMLReader with custom EntityResolver
46-
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
56+
/**
57+
* Step 1a: Pure function to load a resource from classpath.
58+
* Used by loadTransformationSources and performTransformation.
59+
*/
60+
private Optional<InputStream> loadResource(String fileName) {
61+
return Optional.ofNullable(
62+
getClass().getClassLoader().getResourceAsStream(fileName)
63+
);
64+
}
65+
66+
/**
67+
* Step 2: Creates SAXSource with custom EntityResolver.
68+
* Pure function that creates immutable SAXSource using modern SAX API.
69+
*/
70+
private SAXSource createSaxSource(TransformationSources sources) {
71+
try {
72+
// Modern approach: Use SAXParserFactory instead of deprecated XMLReaderFactory
73+
XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
4774
xmlReader.setEntityResolver(new ResourceEntityResolver());
75+
return new SAXSource(xmlReader, new InputSource(sources.xmlStream()));
76+
} catch (SAXException | ParserConfigurationException e) {
77+
throw new RuntimeException("Failed to create SAX source with modern XMLReader API", e);
78+
}
79+
}
4880

49-
// Create SAXSource with custom XMLReader
50-
SAXSource xmlSource = new SAXSource(xmlReader, new InputSource(xmlStream));
81+
/**
82+
* Step 3: Performs the actual XSLT transformation.
83+
* Returns Optional to handle transformation failures gracefully.
84+
*/
85+
private Optional<String> performTransformation(SAXSource xmlSource, String xslFileName) {
86+
return loadResource(xslFileName)
87+
.flatMap(xslStream -> executeTransformation(xmlSource, xslStream));
88+
}
5189

52-
// Create transformer factory and transformer
90+
/**
91+
* Step 4: Executes the transformation and returns the result.
92+
* Encapsulates the transformation logic in a pure function.
93+
*/
94+
private Optional<String> executeTransformation(SAXSource xmlSource, InputStream xslStream) {
95+
try {
5396
TransformerFactory factory = TransformerFactory.newInstance();
5497
Transformer transformer = factory.newTransformer(new StreamSource(xslStream));
5598

56-
// Prepare result
5799
StringWriter stringWriter = new StringWriter();
58100
Result result = new StreamResult(stringWriter);
59101

60-
// Perform transformation
61102
transformer.transform(xmlSource, result);
62103

63-
// Return the transformed content
64-
return stringWriter.toString().trim();
104+
return Optional.of(stringWriter.toString().trim());
105+
} catch (TransformerException e) {
106+
// Log the exception in a real application
107+
return Optional.empty();
108+
}
109+
}
110+
111+
// ===============================================================
112+
// SUPPORTING CLASSES - Used by the main processing pipeline
113+
// ===============================================================
65114

66-
} catch (Exception e) {
67-
throw new RuntimeException("Error during XML transformation", e);
115+
/**
116+
* Record for holding transformation sources - immutable data transfer (internal use only).
117+
* Used by loadTransformationSources to bundle XML and XSL streams together.
118+
*/
119+
private record TransformationSources(InputStream xmlStream, InputStream xslStream) {
120+
private TransformationSources {
121+
if (Objects.isNull(xmlStream) || Objects.isNull(xslStream)) {
122+
throw new IllegalArgumentException("XML and XSL streams cannot be null");
123+
}
68124
}
69125
}
70126

127+
/**
128+
* Custom EntityResolver as functional interface implementation.
129+
* Used by createSaxSource to resolve DTD references.
130+
*/
131+
private static final class ResourceEntityResolver implements EntityResolver {
132+
@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"
139+
}
140+
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+
});
149+
}
150+
}
71151
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?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">
5+
<metadata>
6+
<description></description>
7+
<globs></globs>
8+
<always-apply>false</always-apply>
9+
<tags>
10+
<tag>java</tag>
11+
<tag>checklist</tag>
12+
<tag>cursor-rules</tag>
13+
</tags>
14+
</metadata>
15+
16+
<header>
17+
<title>Create a Checklist with all Java steps to use with cursor rules for Java</title>
18+
</header>
19+
20+
<system-characterization>
21+
<role-definition>You are a Senior software engineer with extensive experience in Java programming language and technical documentation</role-definition>
22+
</system-characterization>
23+
24+
<description>
25+
Your task is to create a comprehensive step-by-step guide that follows the exact format and structure defined in the embedded template below.
26+
</description>
27+
28+
<content-sections>
29+
<instruction-section>
30+
<instruction-header>
31+
<instruction-title>Context</instruction-title>
32+
</instruction-header>
33+
<instruction-description>You have access to a set of cursor rules that java development. You need to create a structured guide that helps new users navigate through the entire set of java cursor rules.</instruction-description>
34+
<instruction-rules>
35+
</instruction-rules>
36+
</instruction-section>
37+
38+
<instruction-section>
39+
<instruction-header>
40+
<instruction-title>Template Structure (Self-Contained)</instruction-title>
41+
</instruction-header>
42+
<instruction-description>Create a markdown file named `JAVA-DEVELOPMENT-GUIDE.md` with the following exact structure: [java-checklist-template.md](mdc:.cursor/rules/templates/java-checklist-template.md)</instruction-description>
43+
<instruction-rules>
44+
</instruction-rules>
45+
</instruction-section>
46+
47+
<instruction-section>
48+
<instruction-header>
49+
<instruction-title>CRITICAL: Strict Template Adherence</instruction-title>
50+
</instruction-header>
51+
<instruction-description>**MANDATORY REQUIREMENT**: Follow the embedded template EXACTLY - do not add, remove, or modify any steps, sections, or cursor rules that are not explicitly shown in the template. ### What NOT to Include:</instruction-description>
52+
<instruction-rules>
53+
<instruction-rule type="mandatory">**DO NOT** create additional steps beyond what's shown in the template</instruction-rule>
54+
<instruction-rule type="mandatory">**DO NOT** modify the numbering system or step structure from the template</instruction-rule>
55+
<instruction-rule type="mandatory">**DO NOT** add cursor rules that are not explicitly listed in the embedded template</instruction-rule>
56+
<instruction-rule type="mandatory">**DO NOT** expand or elaborate on sections beyond what the template shows</instruction-rule>
57+
</instruction-rules>
58+
</instruction-section>
59+
60+
<instruction-section>
61+
<instruction-header>
62+
<instruction-title>Template Boundaries</instruction-title>
63+
</instruction-header>
64+
<instruction-description>### Template Boundaries:</instruction-description>
65+
<instruction-rules>
66+
<instruction-rule type="mandatory">**ONLY** use cursor rules that appear in the embedded template</instruction-rule>
67+
<instruction-rule type="mandatory">**ONLY** create the exact number of steps shown in the template (should be 6 steps, not more)</instruction-rule>
68+
<instruction-rule type="mandatory">**ONLY** use the exact wording and structure from the template</instruction-rule>
69+
<instruction-rule type="mandatory">**ONLY** include cursor rules explicitly present in the template reference table</instruction-rule>
70+
<instruction-rule type="conditional">If a cursor rule exists in the workspace but is not in the template, **DO NOT** include it</instruction-rule>
71+
</instruction-rules>
72+
</instruction-section>
73+
74+
<instruction-section>
75+
<instruction-header>
76+
<instruction-title>Instructions for AI</instruction-title>
77+
</instruction-header>
78+
<instruction-rules>
79+
<instruction-rule>**Follow the exact format** shown in the template above</instruction-rule>
80+
<instruction-rule>**Use the specific numbering system** (1.1, 1.2, etc.) as shown</instruction-rule>
81+
<instruction-rule>**Include all the bash commands exactly** as specified in the template</instruction-rule>
82+
<instruction-rule>**Maintain the checkbox structure** for progress tracking</instruction-rule>
83+
<instruction-rule>**Keep all notes and warnings** from the original PROMPTS.md format</instruction-rule>
84+
<instruction-rule>**Add the reference table and best practices** as shown in the template</instruction-rule>
85+
<instruction-rule>**Make it self-contained** - no external references needed</instruction-rule>
86+
</instruction-rules>
87+
</instruction-section>
88+
89+
<template-section type="configuration">
90+
<template-header>
91+
<template-title>Pre-Generation Validation Checklist</template-title>
92+
</template-header>
93+
<template-description>Before generating the guide, verify:</template-description>
94+
<template-content>
95+
<code-block>
96+
- [ ] All steps match the template exactly (no more, no less)
97+
- [ ] All cursor rules included are present in the template's reference table
98+
- [ ] No additional framework-specific rules added beyond template scope
99+
- [ ] Step numbering system matches template structure
100+
- [ ] Progress tracking section mirrors template format
101+
- [ ] Tips &amp; Best Practices section uses template content only
102+
</code-block>
103+
</template-content>
104+
</template-section>
105+
106+
<instruction-section>
107+
<instruction-header>
108+
<instruction-title>Output Requirements</instruction-title>
109+
</instruction-header>
110+
<instruction-rules>
111+
<instruction-rule>Generate the complete markdown file following the embedded template exactly</instruction-rule>
112+
<instruction-rule>Include all sections: Prerequisites, Process Overview, Reference Table, Tips, Progress Tracking</instruction-rule>
113+
<instruction-rule>Use proper markdown formatting with headers, code blocks, tables, and checklists</instruction-rule>
114+
<instruction-rule>Ensure the guide is beginner-friendly but comprehensive</instruction-rule>
115+
<instruction-rule>Make it portable to any repository without dependencies</instruction-rule>
116+
<instruction-rule>**VERIFY**: Final output contains ONLY what appears in the embedded template</instruction-rule>
117+
</instruction-rules>
118+
</instruction-section>
119+
</content-sections>
120+
</system-prompt>

spml/src/main/resources/unified-generator.xsl

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@
66
<xsl:template match="/system-prompt">
77
<!-- Common frontmatter and header -->
88
<xsl:text>---
9-
description: </xsl:text><xsl:value-of select="normalize-space(metadata/description)"/>
9+
description:</xsl:text>
10+
<xsl:if test="normalize-space(metadata/description)">
11+
<xsl:text> </xsl:text><xsl:value-of select="normalize-space(metadata/description)"/>
12+
</xsl:if>
1013
<xsl:text>
11-
globs: </xsl:text><xsl:value-of select="normalize-space(metadata/globs)"/>
14+
globs:</xsl:text>
15+
<xsl:if test="normalize-space(metadata/globs)">
16+
<xsl:text> </xsl:text><xsl:value-of select="normalize-space(metadata/globs)"/>
17+
</xsl:if>
1218
<xsl:text>
1319
alwaysApply: </xsl:text><xsl:value-of select="normalize-space(metadata/always-apply)"/>
1420
<xsl:text>
@@ -188,9 +194,22 @@ Description: </xsl:text><xsl:value-of select="normalize-space(rule-description)"
188194
</xsl:text>
189195
<xsl:if test="instruction-description">
190196
<xsl:value-of select="normalize-space(instruction-description)"/>
191-
<xsl:text>
197+
<xsl:choose>
198+
<xsl:when test="normalize-space(instruction-description) = '### Template Boundaries:'">
199+
<xsl:text>
200+
201+
</xsl:text>
202+
</xsl:when>
203+
<xsl:when test="substring(normalize-space(instruction-description), string-length(normalize-space(instruction-description))) = ':'">
204+
<xsl:text>
205+
</xsl:text>
206+
</xsl:when>
207+
<xsl:otherwise>
208+
<xsl:text>
192209

193210
</xsl:text>
211+
</xsl:otherwise>
212+
</xsl:choose>
194213
</xsl:if>
195214
<xsl:for-each select="instruction-rules/instruction-rule">
196215
<xsl:text>- </xsl:text><xsl:value-of select="normalize-space(.)"/>

0 commit comments

Comments
 (0)