1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.rat.tools.xsd;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.io.OutputStreamWriter;
26 import java.io.Writer;
27 import java.nio.charset.StandardCharsets;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32
33 import javax.xml.transform.OutputKeys;
34 import javax.xml.transform.Transformer;
35 import javax.xml.transform.TransformerException;
36 import javax.xml.transform.TransformerFactory;
37 import javax.xml.transform.stream.StreamResult;
38 import javax.xml.transform.stream.StreamSource;
39
40 import org.apache.rat.commandline.StyleSheets;
41 import org.apache.rat.config.parameters.ComponentType;
42 import org.apache.rat.config.parameters.Description;
43 import org.apache.rat.config.parameters.DescriptionBuilder;
44 import org.apache.rat.configuration.MatcherBuilderTracker;
45 import org.apache.rat.configuration.XMLConfig;
46 import org.apache.rat.license.SimpleLicense;
47 import org.apache.rat.tools.xsd.XsdWriter.Type;
48
49
50
51
52 public class XsdGenerator {
53
54 private XsdWriter writer;
55
56 private static final Map<ComponentType, String> TYPE_MAP = new HashMap<>();
57
58 static {
59 TYPE_MAP.put(ComponentType.MATCHER, XMLConfig.MATCHER);
60 TYPE_MAP.put(ComponentType.PARAMETER, "xs:string");
61 TYPE_MAP.put(ComponentType.LICENSE, XMLConfig.LICENSE);
62 }
63
64
65
66
67
68
69
70
71 public static void main(final String[] args) throws IOException, TransformerException {
72 XsdGenerator generator = new XsdGenerator();
73
74 TransformerFactory tf = TransformerFactory.newInstance();
75 Transformer transformer;
76 try (InputStream in = generator.getInputStream();
77 InputStream styleIn = StyleSheets.XML.getStyleSheet().get()) {
78 transformer = tf.newTransformer(new StreamSource(styleIn));
79 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
80 transformer.setOutputProperty(OutputKeys.METHOD, "xml");
81 transformer.setOutputProperty(OutputKeys.INDENT, "yes");
82 transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
83 transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
84 transformer.transform(new StreamSource(in),
85 new StreamResult(new OutputStreamWriter(System.out, StandardCharsets.UTF_8)));
86 }
87 }
88
89
90
91
92
93
94 public InputStream getInputStream() throws IOException {
95 try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
96 Writer writer = new OutputStreamWriter(baos)) {
97 write(writer);
98 return new ByteArrayInputStream(baos.toByteArray());
99 }
100 }
101
102
103
104
105
106
107 public void write(final Writer output) throws IOException {
108 writer = new XsdWriter(output).init();
109
110 writer.open(Type.ELEMENT, "name", XMLConfig.ROOT)
111 .open(Type.COMPLEX).open(Type.SEQUENCE);
112 writeFamilies();
113 writeLicenses();
114 writeApproved();
115 writeMatchers();
116 writer.close(Type.ELEMENT);
117
118 writeMatcherElements();
119
120 writer.finish();
121 }
122
123 private void writeFamilies() throws IOException {
124 writer.open(Type.ELEMENT, "name", XMLConfig.FAMILIES, "maxOccurs", "1", "minOccurs", "0")
125 .open(Type.COMPLEX)
126 .open(Type.SEQUENCE)
127 .open(Type.ELEMENT, "name", XMLConfig.FAMILY, "maxOccurs", "unbounded", "minOccurs", "0")
128 .open(Type.COMPLEX)
129 .attribute(XMLConfig.ATT_ID, "type", "xs:string", "use", "required")
130 .attribute(XMLConfig.ATT_NAME, "type", "xs:string", "use", "required")
131 .close(Type.ELEMENT)
132 .close(Type.ELEMENT);
133 }
134
135 private void writeLicenses() throws IOException {
136 Description desc = DescriptionBuilder.buildMap(SimpleLicense.class);
137 List<Description> children = new ArrayList<>();
138 List<Description> attributes = new ArrayList<>();
139
140 if (desc != null && desc.getChildren() != null) {
141 for (Description child : desc.getChildren().values()) {
142 if (XMLConfig.isLicenseChild(child.getCommonName())) {
143 children.add(child);
144 } else {
145 if (child.getType() == ComponentType.PARAMETER) {
146 attributes.add(child);
147 }
148 }
149 }
150 }
151 writer.open(Type.ELEMENT, "name", XMLConfig.LICENSES, "maxOccurs", "1", "minOccurs", "0")
152 .open(Type.COMPLEX).open(Type.SEQUENCE)
153 .open(Type.ELEMENT, "name", XMLConfig.LICENSE, "maxOccurs", "unbounded", "minOccurs", "0")
154 .open(Type.COMPLEX).open(Type.CHOICE, "maxOccurs", "unbounded", "minOccurs", "1");
155 for (Description child : children) {
156 if (child.getCommonName().equals("matcher")) {
157 writer.open(Type.ELEMENT, "ref", XMLConfig.MATCHER, "maxOccurs", "1", "minOccurs", "1").close(Type.ELEMENT);
158 } else {
159 element(child);
160 }
161 }
162 writer.close(Type.CHOICE);
163 for (Description child : attributes) {
164 attribute(child);
165 }
166 writer.close(Type.ELEMENT).close(Type.ELEMENT);
167 }
168
169 private void writeApproved() throws IOException {
170 writer.open(Type.ELEMENT, "name", XMLConfig.APPROVED, "maxOccurs", "1", "minOccurs", "0")
171 .open(Type.COMPLEX).open(Type.SEQUENCE)
172 .open(Type.ELEMENT, "name", XMLConfig.FAMILY, "maxOccurs", "unbounded", "minOccurs", "0")
173 .open(Type.COMPLEX)
174 .attribute(XMLConfig.ATT_LICENSE_REF, "type", "xs:string", "use", "required")
175 .close(Type.ELEMENT)
176 .close(Type.ELEMENT);
177 }
178
179 private void writeMatchers() throws IOException {
180 writer.open(Type.ELEMENT, "name", XMLConfig.MATCHERS, "maxOccurs", "1", "minOccurs", "0")
181 .open(Type.COMPLEX).open(Type.SEQUENCE)
182 .open(Type.ELEMENT, "name", XMLConfig.MATCHER, "maxOccurs", "unbounded", "minOccurs", "0")
183 .open(Type.COMPLEX)
184 .attribute(XMLConfig.ATT_CLASS_NAME, "type", "xs:string", "use", "required")
185 .close(Type.ELEMENT)
186 .close(Type.ELEMENT);
187 }
188
189 private void writeMatcherElements() throws IOException {
190 MatcherBuilderTracker tracker = MatcherBuilderTracker.instance();
191 writer.open(Type.ELEMENT, "name", XMLConfig.MATCHER, "abstract", "true").close(Type.ELEMENT);
192
193
194 for (Class<?> clazz : tracker.getClasses()) {
195 Description desc = DescriptionBuilder.buildMap(clazz);
196 if (desc != null) {
197 boolean hasResourceAttr = false;
198 Description inline = null;
199 List<Description> attributes = new ArrayList<>();
200 for (Description child : desc.getChildren().values()) {
201 if (XMLConfig.isInlineNode(desc.getCommonName(), child.getCommonName())) {
202 inline = child;
203 } else {
204 hasResourceAttr |= child.getCommonName().equals(XMLConfig.ATT_RESOURCE);
205 attributes.add(child);
206 }
207 }
208 writer.open(Type.ELEMENT, "name", desc.getCommonName(), "substitutionGroup", XMLConfig.MATCHER)
209 .open(Type.COMPLEX);
210 if (inline != null) {
211 if ("enclosed".equals(inline.getCommonName())) {
212 writer.open(Type.CHOICE).open(Type.ELEMENT, "ref", XMLConfig.MATCHER, "maxOccurs",
213 inline.isCollection() ? "unbounded" : "1", "minOccurs", hasResourceAttr ? "0" : "1")
214 .close(Type.CHOICE);
215 } else {
216 writer.open(Type.SIMPLE).open(Type.EXTENSION, "base", "xs:string");
217 }
218 }
219 for (Description child : attributes) {
220 attribute(child);
221 }
222 writer.close(Type.ELEMENT);
223 }
224 }
225 }
226
227 private void element(final Description desc) throws IOException {
228 String typeName = TYPE_MAP.get(desc.getType());
229 if (typeName != null) {
230 writer.open(Type.ELEMENT,
231 "name", desc.getCommonName(),
232 "type", typeName,
233 "minOccurs", desc.isRequired() ? "1" : "0",
234 "maxOccurs", desc.isCollection() ? "unbounded" : "1"
235 ).close(Type.ELEMENT);
236 }
237 }
238
239 private void attribute(final Description attr) throws IOException {
240 if (attr.getType() == ComponentType.PARAMETER) {
241 writer.attribute(attr.getCommonName(), "form", "unqualified", "use", attr.isRequired() ? "required" : "optional");
242 }
243 }
244 }