001: package org.kohsuke.rngom.digested;
002:
003: import java.io.FileOutputStream;
004: import java.io.OutputStream;
005: import java.util.List;
006:
007: import javax.xml.namespace.QName;
008: import javax.xml.stream.XMLOutputFactory;
009: import javax.xml.stream.XMLStreamException;
010: import javax.xml.stream.XMLStreamWriter;
011:
012: import org.kohsuke.rngom.ast.builder.BuildException;
013: import org.kohsuke.rngom.ast.builder.SchemaBuilder;
014: import org.kohsuke.rngom.ast.util.CheckingSchemaBuilder;
015: import org.kohsuke.rngom.nc.NameClass;
016: import org.kohsuke.rngom.nc.NameClassVisitor;
017: import org.kohsuke.rngom.nc.SimpleNameClass;
018: import org.kohsuke.rngom.parse.Parseable;
019: import org.kohsuke.rngom.parse.compact.CompactParseable;
020: import org.kohsuke.rngom.parse.xml.SAXParseable;
021: import org.kohsuke.rngom.xml.util.WellKnownNamespaces;
022: import org.w3c.dom.Element;
023: import org.w3c.dom.Node;
024: import org.xml.sax.ErrorHandler;
025: import org.xml.sax.InputSource;
026: import org.xml.sax.SAXException;
027: import org.xml.sax.SAXParseException;
028: import org.xml.sax.helpers.DefaultHandler;
029:
030: /**
031: * Printer of RELAX NG digested model to XML using StAX {@link XMLStreamWriter}.
032: *
033: * @author <A href="mailto:demakov@ispras.ru">Alexey Demakov</A>
034: */
035: public class DXMLPrinter {
036: protected XMLStreamWriter out;
037: protected String indentStep = "\t";
038: protected String newLine = System.getProperty("line.separator");
039: protected int indent;
040: protected boolean afterEnd = false;
041: protected DXMLPrinterVisitor visitor;
042: protected NameClassXMLPrinterVisitor ncVisitor;
043: protected DOMPrinter domPrinter;
044:
045: /**
046: * @param out Output stream.
047: */
048: public DXMLPrinter(XMLStreamWriter out) {
049: this .out = out;
050: this .visitor = new DXMLPrinterVisitor();
051: this .ncVisitor = new NameClassXMLPrinterVisitor();
052: this .domPrinter = new DOMPrinter(out);
053: }
054:
055: /**
056: * Prints grammar enclosed by start/end document.
057: *
058: * @param grammar
059: * @throws XMLStreamException
060: */
061: public void printDocument(DGrammarPattern grammar)
062: throws XMLStreamException {
063: try {
064: visitor.startDocument();
065: visitor.on(grammar);
066: visitor.endDocument();
067: } catch (XMLWriterException e) {
068: throw (XMLStreamException) e.getCause();
069: }
070: }
071:
072: /**
073: * Prints XML fragment for the given pattern.
074: *
075: * @throws XMLStreamException
076: */
077: public void print(DPattern pattern) throws XMLStreamException {
078: try {
079: pattern.accept(visitor);
080: } catch (XMLWriterException e) {
081: throw (XMLStreamException) e.getCause();
082: }
083: }
084:
085: /**
086: * Prints XML fragment for the given name class.
087: *
088: * @throws XMLStreamException
089: */
090: public void print(NameClass nc) throws XMLStreamException {
091: try {
092: nc.accept(ncVisitor);
093: } catch (XMLWriterException e) {
094: throw (XMLStreamException) e.getCause();
095: }
096: }
097:
098: public void print(Node node) throws XMLStreamException {
099: domPrinter.print(node);
100: }
101:
102: protected class XMLWriterException extends RuntimeException {
103: protected XMLWriterException(Throwable cause) {
104: super (cause);
105: }
106: }
107:
108: protected class XMLWriter {
109: protected void newLine() {
110: try {
111: out.writeCharacters(newLine);
112: } catch (XMLStreamException e) {
113: throw new XMLWriterException(e);
114: }
115: }
116:
117: protected void indent() {
118: try {
119: for (int i = 0; i < indent; i++) {
120: out.writeCharacters(indentStep);
121: }
122: } catch (XMLStreamException e) {
123: throw new XMLWriterException(e);
124: }
125: }
126:
127: public void startDocument() {
128: try {
129: out.writeStartDocument();
130: } catch (XMLStreamException e) {
131: throw new XMLWriterException(e);
132: }
133: }
134:
135: public void endDocument() {
136: try {
137: out.writeEndDocument();
138: } catch (XMLStreamException e) {
139: throw new XMLWriterException(e);
140: }
141: }
142:
143: public final void start(String element) {
144: try {
145: newLine();
146: indent();
147: out.writeStartElement(element);
148: indent++;
149: afterEnd = false;
150: } catch (XMLStreamException e) {
151: throw new XMLWriterException(e);
152: }
153: }
154:
155: public void end() {
156: try {
157: indent--;
158: if (afterEnd) {
159: newLine();
160: indent();
161: }
162: out.writeEndElement();
163: afterEnd = true;
164: } catch (XMLStreamException e) {
165: throw new XMLWriterException(e);
166: }
167: }
168:
169: public void attr(String prefix, String ns, String name,
170: String value) {
171: try {
172: out.writeAttribute(prefix, ns, name, value);
173: } catch (XMLStreamException e) {
174: throw new XMLWriterException(e);
175: }
176: }
177:
178: public void attr(String name, String value) {
179: try {
180: out.writeAttribute(name, value);
181: } catch (XMLStreamException e) {
182: throw new XMLWriterException(e);
183: }
184: }
185:
186: public void ns(String prefix, String uri) {
187: try {
188: out.writeNamespace(prefix, uri);
189: } catch (XMLStreamException e) {
190: throw new XMLWriterException(e);
191: }
192: }
193:
194: public void body(String text) {
195: try {
196: out.writeCharacters(text);
197: afterEnd = false;
198: } catch (XMLStreamException e) {
199: throw new XMLWriterException(e);
200: }
201: }
202: }
203:
204: protected class DXMLPrinterVisitor extends XMLWriter implements
205: DPatternVisitor<Void> {
206: protected void on(DPattern p) {
207: p.accept(this );
208: }
209:
210: protected void unwrapGroup(DPattern p) {
211: if (p instanceof DGroupPattern
212: && p.getAnnotation() == DAnnotation.EMPTY) {
213: for (DPattern d : (DGroupPattern) p) {
214: on(d);
215: }
216: } else {
217: on(p);
218: }
219: }
220:
221: protected void unwrapChoice(DPattern p) {
222: if (p instanceof DChoicePattern
223: && p.getAnnotation() == DAnnotation.EMPTY) {
224: for (DPattern d : (DChoicePattern) p) {
225: on(d);
226: }
227: } else {
228: on(p);
229: }
230: }
231:
232: protected void on(NameClass nc) {
233: if (nc instanceof SimpleNameClass) {
234: QName qname = ((SimpleNameClass) nc).name;
235: String name = qname.getLocalPart();
236: if (!qname.getPrefix().equals(""))
237: name = qname.getPrefix() + ":";
238: attr("name", name);
239: } else {
240: nc.accept(ncVisitor);
241: }
242: }
243:
244: protected void on(DAnnotation ann) {
245: if (ann == DAnnotation.EMPTY)
246: return;
247: for (DAnnotation.Attribute attr : ann.getAttributes()
248: .values()) {
249: attr(attr.getPrefix(), attr.getNs(), attr
250: .getLocalName(), attr.getValue());
251: }
252: for (Element elem : ann.getChildren()) {
253: try {
254: newLine();
255: indent();
256: print(elem);
257: } catch (XMLStreamException e) {
258: throw new XMLWriterException(e);
259: }
260: }
261: }
262:
263: public Void onAttribute(DAttributePattern p) {
264: start("attribute");
265: on(p.getName());
266: on(p.getAnnotation());
267: DPattern child = p.getChild();
268: // do not print default value
269: if (!(child instanceof DTextPattern)) {
270: on(p.getChild());
271: }
272: end();
273: return null;
274: }
275:
276: public Void onChoice(DChoicePattern p) {
277: start("choice");
278: on(p.getAnnotation());
279: for (DPattern d : p) {
280: on(d);
281: }
282: end();
283: return null;
284: }
285:
286: public Void onData(DDataPattern p) {
287: List<DDataPattern.Param> params = p.getParams();
288: DPattern except = p.getExcept();
289: start("data");
290: attr("datatypeLibrary", p.getDatatypeLibrary());
291: attr("type", p.getType());
292: on(p.getAnnotation());
293: for (DDataPattern.Param param : params) {
294: start("param");
295: attr("ns", param.getNs());
296: attr("name", param.getName());
297: body(param.getValue());
298: end();
299: }
300: if (except != null) {
301: start("except");
302: unwrapChoice(except);
303: end();
304: }
305: end();
306: return null;
307: }
308:
309: public Void onElement(DElementPattern p) {
310: start("element");
311: on(p.getName());
312: on(p.getAnnotation());
313: unwrapGroup(p.getChild());
314: end();
315: return null;
316: }
317:
318: public Void onEmpty(DEmptyPattern p) {
319: start("empty");
320: on(p.getAnnotation());
321: end();
322: return null;
323: }
324:
325: public Void onGrammar(DGrammarPattern p) {
326: start("grammar");
327: ns(null, WellKnownNamespaces.RELAX_NG);
328: on(p.getAnnotation());
329: start("start");
330: on(p.getStart());
331: end();
332: for (DDefine d : p) {
333: start("define");
334: attr("name", d.getName());
335: on(d.getAnnotation());
336: unwrapGroup(d.getPattern());
337: end();
338: }
339: end();
340: return null;
341: }
342:
343: public Void onGroup(DGroupPattern p) {
344: start("group");
345: on(p.getAnnotation());
346: for (DPattern d : p) {
347: on(d);
348: }
349: end();
350: return null;
351: }
352:
353: public Void onInterleave(DInterleavePattern p) {
354: start("interleave");
355: on(p.getAnnotation());
356: for (DPattern d : p) {
357: on(d);
358: }
359: end();
360: return null;
361: }
362:
363: public Void onList(DListPattern p) {
364: start("list");
365: on(p.getAnnotation());
366: unwrapGroup(p.getChild());
367: end();
368: return null;
369: }
370:
371: public Void onMixed(DMixedPattern p) {
372: start("mixed");
373: on(p.getAnnotation());
374: unwrapGroup(p.getChild());
375: end();
376: return null;
377: }
378:
379: public Void onNotAllowed(DNotAllowedPattern p) {
380: start("notAllowed");
381: on(p.getAnnotation());
382: end();
383: return null;
384: }
385:
386: public Void onOneOrMore(DOneOrMorePattern p) {
387: start("oneOrMore");
388: on(p.getAnnotation());
389: unwrapGroup(p.getChild());
390: end();
391: return null;
392: }
393:
394: public Void onOptional(DOptionalPattern p) {
395: start("optional");
396: on(p.getAnnotation());
397: unwrapGroup(p.getChild());
398: end();
399: return null;
400: }
401:
402: public Void onRef(DRefPattern p) {
403: start("ref");
404: attr("name", p.getName());
405: on(p.getAnnotation());
406: end();
407: return null;
408: }
409:
410: public Void onText(DTextPattern p) {
411: start("text");
412: on(p.getAnnotation());
413: end();
414: return null;
415: }
416:
417: public Void onValue(DValuePattern p) {
418: start("value");
419: if (!p.getNs().equals(""))
420: attr("ns", p.getNs());
421: attr("datatypeLibrary", p.getDatatypeLibrary());
422: attr("type", p.getType());
423: on(p.getAnnotation());
424: body(p.getValue());
425: end();
426: return null;
427: }
428:
429: public Void onZeroOrMore(DZeroOrMorePattern p) {
430: start("zeroOrMore");
431: on(p.getAnnotation());
432: unwrapGroup(p.getChild());
433: end();
434: return null;
435: }
436: }
437:
438: protected class NameClassXMLPrinterVisitor extends XMLWriter
439: implements NameClassVisitor<Void> {
440: public Void visitChoice(NameClass nc1, NameClass nc2) {
441: // TODO: flatten nested choices
442: start("choice");
443: nc1.accept(this );
444: nc2.accept(this );
445: end();
446: return null;
447: }
448:
449: public Void visitNsName(String ns) {
450: start("nsName");
451: attr("ns", ns);
452: end();
453: return null;
454: }
455:
456: public Void visitNsNameExcept(String ns, NameClass nc) {
457: start("nsName");
458: attr("ns", ns);
459: start("except");
460: nc.accept(this );
461: end();
462: end();
463: return null;
464: }
465:
466: public Void visitAnyName() {
467: start("anyName");
468: end();
469: return null;
470: }
471:
472: public Void visitAnyNameExcept(NameClass nc) {
473: start("anyName");
474: start("except");
475: nc.accept(this );
476: end();
477: end();
478: return null;
479: }
480:
481: public Void visitName(QName name) {
482: start("name");
483: if (!name.getPrefix().equals("")) {
484: body(name.getPrefix() + ":");
485: }
486: body(name.getLocalPart());
487: end();
488: return null;
489: }
490:
491: public Void visitNull() {
492: throw new UnsupportedOperationException("visitNull");
493: }
494: }
495:
496: public static void main(String[] args) throws Exception {
497: Parseable p;
498:
499: ErrorHandler eh = new DefaultHandler() {
500: public void error(SAXParseException e) throws SAXException {
501: throw e;
502: }
503: };
504:
505: // the error handler passed to Parseable will receive parsing errors.
506: if (args[0].endsWith(".rng")) {
507: p = new SAXParseable(new InputSource(args[0]), eh);
508: } else {
509: p = new CompactParseable(new InputSource(args[0]), eh);
510: }
511:
512: // the error handler passed to CheckingSchemaBuilder will receive additional
513: // errors found during the RELAX NG restrictions check.
514: // typically you'd want to pass in the same error handler,
515: // as there's really no distinction between those two kinds of errors.
516: SchemaBuilder sb = new CheckingSchemaBuilder(
517: new DSchemaBuilderImpl(), eh);
518: try {
519: // run the parser
520: DGrammarPattern grammar = (DGrammarPattern) p.parse(sb);
521: OutputStream out = new FileOutputStream(args[1]);
522: XMLOutputFactory factory = XMLOutputFactory.newInstance();
523: XMLStreamWriter output = factory.createXMLStreamWriter(out);
524: DXMLPrinter printer = new DXMLPrinter(output);
525: printer.printDocument(grammar);
526: output.close();
527: out.close();
528: } catch (BuildException e) {
529: if (e.getCause() instanceof SAXParseException) {
530: SAXParseException se = (SAXParseException) e.getCause();
531: System.out.println("(" + se.getLineNumber() + ","
532: + se.getColumnNumber() + "): "
533: + se.getMessage());
534: return;
535: } else
536: // I found that Crimson doesn't show the proper stack trace
537: // when a RuntimeException happens inside a SchemaBuilder.
538: // the following code shows the actual exception that happened.
539: if (e.getCause() instanceof SAXException) {
540: SAXException se = (SAXException) e.getCause();
541: if (se.getException() != null)
542: se.getException().printStackTrace();
543: }
544: throw e;
545: }
546: }
547: }
|