1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.nuiton.jrst;
25
26 import java.io.IOException;
27 import java.io.Writer;
28 import java.net.URL;
29 import java.util.LinkedList;
30 import javax.xml.transform.Transformer;
31 import javax.xml.transform.TransformerException;
32 import javax.xml.transform.TransformerFactory;
33 import javax.xml.transform.URIResolver;
34 import javax.xml.transform.sax.SAXResult;
35 import javax.xml.transform.stream.StreamResult;
36 import javax.xml.transform.stream.StreamSource;
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 import org.dom4j.Document;
40 import org.dom4j.Element;
41 import org.dom4j.Node;
42 import org.dom4j.Text;
43 import org.dom4j.io.DocumentResult;
44 import org.dom4j.io.DocumentSource;
45 import org.nuiton.jrst.legacy.DocumentHandler;
46 import org.nuiton.jrst.legacy.DocumentWalker;
47 import org.nuiton.util.FasterCachedResourceResolver;
48 import org.xml.sax.ContentHandler;
49
50 import static org.nuiton.jrst.legacy.ReStructuredText.ADDRESS;
51 import static org.nuiton.jrst.legacy.ReStructuredText.AUTHOR;
52 import static org.nuiton.jrst.legacy.ReStructuredText.AUTHORS;
53 import static org.nuiton.jrst.legacy.ReStructuredText.BULLET_LIST;
54 import static org.nuiton.jrst.legacy.ReStructuredText.CLASSIFIER;
55 import static org.nuiton.jrst.legacy.ReStructuredText.CONTACT;
56 import static org.nuiton.jrst.legacy.ReStructuredText.COPYRIGHT;
57 import static org.nuiton.jrst.legacy.ReStructuredText.DATE;
58 import static org.nuiton.jrst.legacy.ReStructuredText.DEFINITION;
59 import static org.nuiton.jrst.legacy.ReStructuredText.DEFINITION_LIST;
60 import static org.nuiton.jrst.legacy.ReStructuredText.DOCINFO;
61 import static org.nuiton.jrst.legacy.ReStructuredText.EMPHASIS;
62 import static org.nuiton.jrst.legacy.ReStructuredText.ENUMERATED_LIST;
63 import static org.nuiton.jrst.legacy.ReStructuredText.FIELD_BODY;
64 import static org.nuiton.jrst.legacy.ReStructuredText.FIELD_LIST;
65 import static org.nuiton.jrst.legacy.ReStructuredText.FIELD_NAME;
66 import static org.nuiton.jrst.legacy.ReStructuredText.LIST_ITEM;
67 import static org.nuiton.jrst.legacy.ReStructuredText.LITERAL;
68 import static org.nuiton.jrst.legacy.ReStructuredText.LITERAL_BLOCK;
69 import static org.nuiton.jrst.legacy.ReStructuredText.ORGANIZATION;
70 import static org.nuiton.jrst.legacy.ReStructuredText.PARAGRAPH;
71 import static org.nuiton.jrst.legacy.ReStructuredText.REVISION;
72 import static org.nuiton.jrst.legacy.ReStructuredText.SECTION;
73 import static org.nuiton.jrst.legacy.ReStructuredText.STATUS;
74 import static org.nuiton.jrst.legacy.ReStructuredText.STRONG;
75 import static org.nuiton.jrst.legacy.ReStructuredText.TABLE;
76 import static org.nuiton.jrst.legacy.ReStructuredText.TITLE;
77 import static org.nuiton.jrst.legacy.ReStructuredText.TITLE_CHAR;
78 import static org.nuiton.jrst.legacy.ReStructuredText.TRANSITION;
79 import static org.nuiton.jrst.legacy.ReStructuredText.VERSION;
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 public class JRSTGenerator implements DocumentHandler {
101
102 static boolean DEBUG = true;
103
104 static private Log log = LogFactory.getLog(JRSTGenerator.class);
105
106 protected Writer out;
107 protected int sectionLevel;
108 protected int indent;
109
110 protected String listType = "bullet|enumerated|...";
111 protected int enumStart = 1;
112
113 protected URIResolver uriResolver;
114
115 public JRSTGenerator() {
116 }
117
118 public JRSTGenerator(Writer out) {
119 this.out = out;
120 }
121
122
123
124
125 public URIResolver getUriResolver() {
126 return uriResolver;
127 }
128
129
130
131
132
133 public void setUriResolver(URIResolver uriResolver) {
134 this.uriResolver = uriResolver;
135 }
136
137
138
139
140
141
142
143
144 public void generate(Document doc) throws IOException {
145 generate(doc, this);
146 }
147
148 public void generate(Document doc, DocumentHandler handler) {
149 DocumentWalker walker = new DocumentWalker(handler);
150 walker.walk(doc);
151 }
152
153
154
155
156
157
158
159
160
161 public void generate(Document doc, ContentHandler handler)
162 throws IOException, TransformerException {
163
164 TransformerFactory factory = TransformerFactory.newInstance();
165 if (uriResolver != null) {
166 factory.setURIResolver(uriResolver);
167 }
168 Transformer transformer = factory.newTransformer();
169
170
171 DocumentSource source = new DocumentSource(doc);
172 SAXResult result = new SAXResult(handler);
173 transformer.transform(source, result);
174 }
175
176
177
178
179
180
181
182
183
184 public void generate(Document doc, URL stylesheet) throws IOException,
185 TransformerException {
186 generate(doc, stylesheet, out);
187 }
188
189
190
191
192
193
194
195
196
197
198 public void generate(Document doc, URL stylesheet, Writer out)
199 throws IOException, TransformerException {
200
201 TransformerFactory factory = TransformerFactory.newInstance();
202 if (uriResolver != null) {
203 factory.setURIResolver(uriResolver);
204 } else {
205 factory.setURIResolver(new FasterCachedResourceResolver(stylesheet.toString()));
206 }
207 Transformer transformer = factory.newTransformer(new StreamSource(
208 stylesheet.openStream()));
209
210
211 DocumentSource source = new DocumentSource(doc);
212 StreamResult result = new StreamResult(out);
213 transformer.transform(source, result);
214
215 out.flush();
216 }
217
218
219
220
221
222
223
224
225
226
227 public Document transform(Document doc, URL stylesheet)
228 throws TransformerException, IOException {
229
230 if (log.isInfoEnabled()) {
231 log.debug("Transform document using : " + stylesheet);
232 }
233
234
235 TransformerFactory factory = TransformerFactory.newInstance();
236 if (uriResolver != null) {
237 factory.setURIResolver(uriResolver);
238 } else {
239 factory.setURIResolver(new FasterCachedResourceResolver(stylesheet.toString()));
240 }
241
242 Transformer transformer = factory.newTransformer(new StreamSource(
243 stylesheet.openStream()));
244
245
246 DocumentSource source = new DocumentSource(doc);
247 DocumentResult result = new DocumentResult();
248 transformer.transform(source, result);
249
250
251 Document transformedDoc = result.getDocument();
252
253 return transformedDoc;
254 }
255
256 protected String string(String s, int number) {
257 String result = "";
258 for (int i = 0; i < number; i++) {
259 result += s;
260 }
261 return result;
262 }
263
264 protected String enumtype(int i, String type) {
265 String result = null;
266 if ("arabic".equals(type)) {
267 result = String.valueOf(i);
268 } else if ("loweralpha".equals(type)) {
269 result = String.valueOf((char) ((int) 'a' + i));
270 } else if ("upperalpha".equals(type)) {
271 result = String.valueOf((char) ((int) 'A' + i));
272 } else if ("lowerroman".equals(type) || "upperroman".equals(type)) {
273 String[] c = new String[] { "i", "v", "x", "l", "c", "d", "m" };
274 int[] d = new int[] { 1, 5, 10, 50, 100, 500, 1000 };
275 result = "";
276 for (int a = 0; a < c.length; a++) {
277 result = string(c[a], i / d[a]) + result;
278 i = i % d[a];
279 }
280 if ("upperroman".equals(type)) {
281 result = result.toUpperCase();
282 }
283 }
284 return result;
285 }
286
287
288
289
290
291
292
293
294 @SuppressWarnings("unchecked")
295 protected int inlineLength(Element e) {
296 int result = 0;
297 LinkedList<Node> elems = new LinkedList<Node>();
298 elems.addAll(e.content());
299 while (elems.peek() != null) {
300 Node elem = elems.poll();
301 switch (elem.getNodeType()) {
302 case Node.ELEMENT_NODE:
303 elems.addAll(((Element) elem).content());
304 if (EMPHASIS.equals(elem.getName())) {
305 result += 2;
306 } else if (STRONG.equals(elem.getName())) {
307 result += 4;
308 } else if (LITERAL.equals(elem.getName())) {
309 result += 4;
310 }
311 break;
312 case Node.TEXT_NODE:
313 result += elem.getText().length();
314 break;
315 }
316 }
317 return result;
318 }
319
320
321
322
323
324
325 @Override
326 public void startDocument(Document doc) {
327 }
328
329
330
331
332
333
334 @Override
335 public void endDocument(Document doc) {
336 }
337
338
339
340
341
342
343 @Override
344 public void endElement(Element e) {
345 boolean needNewLine = false;
346
347 if (SECTION.equals(e.getName())) {
348 sectionLevel--;
349 needNewLine = true;
350 } else if (PARAGRAPH.equals(e.getName())) {
351 newLine();
352 needNewLine = true;
353 } else if (TITLE.equals(e.getName())) {
354 newLine();
355 if (sectionLevel == 0) {
356 write(string("=", inlineLength(e)));
357 } else {
358 String c = TITLE_CHAR.substring(sectionLevel, sectionLevel + 1);
359 write(string(c, inlineLength(e)));
360 }
361 newLine();
362 needNewLine = true;
363 } else if (EMPHASIS.equals(e.getName())) {
364 write("*");
365 } else if (STRONG.equals(e.getName())) {
366 write("**");
367 } else if (LITERAL.equals(e.getName())) {
368 write("``");
369 } else if (DOCINFO.equals(e.getName())) {
370 needNewLine = true;
371 } else if (LITERAL_BLOCK.equals(e.getName())) {
372 indent--;
373 newLine();
374 needNewLine = true;
375 } else if (TABLE.equals(e.getName())) {
376
377
378
379 newLine();
380 needNewLine = true;
381 } else if (BULLET_LIST.equals(e.getName())) {
382 needNewLine = true;
383 } else if (ENUMERATED_LIST.equals(e.getName())) {
384 needNewLine = true;
385 } else if (FIELD_LIST.equals(e.getName())) {
386 needNewLine = true;
387 } else if (FIELD_BODY.equals(e.getName())) {
388 indent--;
389 } else if (DEFINITION_LIST.equals(e.getName())) {
390 needNewLine = true;
391 } else if (DEFINITION.equals(e.getName())) {
392 indent--;
393 } else if (LIST_ITEM.equals(e.getName())) {
394 indent--;
395 } else if (AUTHOR.equals(e.getName())) {
396 newLine();
397 indent--;
398 } else if (AUTHORS.equals(e.getName())) {
399 newLine();
400 indent--;
401 } else if (ORGANIZATION.equals(e.getName())) {
402 newLine();
403 indent--;
404 } else if (ADDRESS.equals(e.getName())) {
405 newLine();
406 indent--;
407 } else if (CONTACT.equals(e.getName())) {
408 newLine();
409 indent--;
410 } else if (VERSION.equals(e.getName())) {
411 newLine();
412 indent--;
413 } else if (REVISION.equals(e.getName())) {
414 newLine();
415 indent--;
416 } else if (STATUS.equals(e.getName())) {
417 newLine();
418 indent--;
419 } else if (DATE.equals(e.getName())) {
420 newLine();
421 indent--;
422 } else if (COPYRIGHT.equals(e.getName())) {
423 newLine();
424 indent--;
425 }
426
427 if (needNewLine) {
428
429
430
431
432 Element parent = e.getParent();
433 Node lastNode = parent.node(parent.nodeCount() - 1);
434 if (lastNode != e) {
435
436
437 newLine();
438 }
439 }
440 }
441
442
443
444
445
446
447 @Override
448 public void startElement(Element e) {
449 if (SECTION.equals(e.getName())) {
450 sectionLevel++;
451 } else if (TITLE.equals(e.getName())) {
452 if (sectionLevel == 0) {
453 write(string("=", inlineLength(e)));
454 newLine();
455 }
456 } else if (PARAGRAPH.equals(e.getName())) {
457 Element parent = e.getParent();
458 if (!((LIST_ITEM.equals(parent.getName()) || FIELD_BODY
459 .equals(parent.getName())) && parent.node(0) == e)) {
460 writeIndent();
461 }
462 } else if (TRANSITION.equals(e.getName())) {
463 write(string("-", 80));
464 newLine();
465 newLine();
466 } else if (EMPHASIS.equals(e.getName())) {
467 write("*");
468 } else if (STRONG.equals(e.getName())) {
469 write("**");
470 } else if (LITERAL.equals(e.getName())) {
471 write("``");
472 } else if (AUTHOR.equals(e.getName())) {
473 write(":Author: ");
474 indent++;
475 } else if (AUTHORS.equals(e.getName())) {
476 write(":Authors: ");
477 indent++;
478 } else if (ORGANIZATION.equals(e.getName())) {
479 write(":Organization: ");
480 indent++;
481 } else if (ADDRESS.equals(e.getName())) {
482 write(":Address: ");
483 indent++;
484 } else if (CONTACT.equals(e.getName())) {
485 write(":Contact: ");
486 indent++;
487 } else if (VERSION.equals(e.getName())) {
488 write(":Version: ");
489 indent++;
490 } else if (REVISION.equals(e.getName())) {
491 write(":Revision: ");
492 indent++;
493 } else if (STATUS.equals(e.getName())) {
494 write(":Status: ");
495 indent++;
496 } else if (DATE.equals(e.getName())) {
497 write(":Date: ");
498 indent++;
499 } else if (COPYRIGHT.equals(e.getName())) {
500 write(":Copyright: ");
501 indent++;
502 } else if (FIELD_NAME.equals(e.getName())) {
503 writeIndent(":");
504 } else if (FIELD_BODY.equals(e.getName())) {
505 write(": ");
506 indent++;
507 } else if (CLASSIFIER.equals(e.getName())) {
508 write(" : ");
509 } else if (DEFINITION.equals(e.getName())) {
510
511
512
513 newLine();
514 indent++;
515 } else if (LITERAL_BLOCK.equals(e.getName())) {
516 write("::");
517 newLine();
518 newLine();
519 indent++;
520 } else if (TABLE.equals(e.getName())) {
521
522
523
524 } else if (BULLET_LIST.equals(e.getName())) {
525 listType = BULLET_LIST;
526 } else if (ENUMERATED_LIST.equals(e.getName())) {
527 listType = ENUMERATED_LIST;
528 enumStart = Integer.parseInt(e.attributeValue("start"));
529 } else if (LIST_ITEM.equals(e.getName())) {
530 if (BULLET_LIST.equals(listType)) {
531 writeIndent("- ");
532 } else if (ENUMERATED_LIST.equals(listType)) {
533 writeIndent(enumtype(enumStart++, e.getParent().attributeValue(
534 "enumtype"))
535 + ". ");
536 }
537 indent++;
538 }
539 }
540
541
542
543
544
545
546 @Override
547 public void text(Text t) {
548 if (LITERAL_BLOCK.equals(t.getParent().getName())) {
549 writeIndent(t.getText());
550 } else {
551 write(t.getText());
552 }
553 }
554
555 protected void newLine() {
556 write("\n");
557 }
558
559 protected void write(String text) {
560 write(text, false);
561 }
562
563 protected void writeIndent() {
564 write("", true);
565 }
566
567 protected void writeIndent(String text) {
568 write(text, true);
569 }
570
571
572
573
574
575
576
577 protected void write(String text, boolean doIndent) {
578 try {
579 String blank = "";
580 if (doIndent) {
581 blank = string(" ", indent);
582 }
583 out.write(blank);
584 for (char c : text.toCharArray()) {
585 out.write(c);
586 if (c == '\n') {
587 out.write(blank);
588 }
589 }
590 } catch (IOException eee) {
591 if (log.isWarnEnabled()) {
592 log.warn("TODO untreated error", eee);
593 }
594 }
595 }
596 }