001    package com.thaiopensource.relaxng.impl;
002    
003    import org.relaxng.datatype.Datatype;
004    
005    import java.io.OutputStream;
006    import java.io.PrintWriter;
007    import java.util.Hashtable;
008    import java.util.Vector;
009    
010    import com.thaiopensource.xml.util.WellKnownNamespaces;
011    import com.thaiopensource.xml.util.Name;
012    
013    public class PatternDumper {
014      private boolean startTagOpen = false;
015      private final Vector tagStack = new Vector();
016      private final PrintWriter writer;
017      private int level = 0;
018      private boolean suppressIndent = false;
019      private final Vector patternList = new Vector();
020      private final Hashtable patternTable = new Hashtable();
021    
022      private final PatternVisitor patternVisitor = new DumpPatternVisitor();
023      private final PatternVisitor groupPatternVisitor = new GroupDumpPatternVisitor();
024      private final PatternVisitor choicePatternVisitor = new ChoiceDumpPatternVisitor();
025      private final PatternVisitor interleavePatternVisitor = new InterleaveDumpPatternVisitor();
026      private final NameClassVisitor nameClassVisitor = new DumpNameClassVisitor();
027      private final NameClassVisitor choiceNameClassVisitor = new ChoiceDumpNameClassVisitor();
028    
029      static public void dump(PrintWriter writer, Pattern p) {
030        new PatternDumper(writer).dump(p);
031      }
032    
033      static public void dump(OutputStream out, Pattern p) {
034        new PatternDumper(new PrintWriter(out)).dump(p);
035      }
036    
037      private PatternDumper(PrintWriter writer) {
038        this.writer = writer;
039      }
040    
041      private void dump(Pattern p) {
042        write("<?xml version=\"1.0\"?>");
043        startElement("grammar");
044        attribute("xmlns", WellKnownNamespaces.RELAX_NG);
045        startElement("start");
046        p.accept(groupPatternVisitor);
047        endElement();
048        for (int i = 0; i < patternList.size(); i++) {
049          startElement("define");
050          Pattern tem = (Pattern)patternList.elementAt(i);
051          attribute("name", getName(tem));
052          tem.accept(groupPatternVisitor);
053          endElement();
054        }
055        endElement();
056        writer.println();
057        writer.flush();
058      }
059    
060      private String getName(Pattern p) {
061        String name = (String)patternTable.get(p);
062        if (name == null) {
063          name = "p" + patternList.size();
064          patternList.addElement(p);
065          patternTable.put(p, name);
066        }
067        return name;
068      }
069    
070      private void startElement(String name) {
071        closeStartTag();
072        indent(level);
073        write('<');
074        write(name);
075        push(name);
076        startTagOpen = true;
077        level++;
078      }
079    
080      private void closeStartTag() {
081        if (startTagOpen) {
082          startTagOpen = false;
083          write('>');
084        }
085      }
086    
087      private void attribute(String name, String value) {
088        write(' ');
089        write(name);
090        write('=');
091        write('"');
092        chars(value, true);
093        write('"');
094      }
095    
096      private void data(String str) {
097        if (str.length() > 0) {
098          closeStartTag();
099          chars(str, false);
100          suppressIndent = true;
101        }
102      }
103    
104      private void chars(String str, boolean isAttribute) {
105        int len = str.length();
106        for (int i = 0; i < len; i++) {
107          char c = str.charAt(i);
108          switch (c) {
109          case '&':
110            write("&amp;");
111            break;
112          case '<':
113            write("&lt;");
114            break;
115          case '>':
116            write("&gt;");
117            break;
118          case '"':
119            if (isAttribute) {
120              write("&quot;");
121              break;
122            }
123            // fall through
124          default:
125            write(c);
126            break;
127          }
128        }
129      }
130          
131      private void endElement() {
132        --level;
133        if (startTagOpen) {
134          startTagOpen = false;
135          write("/>");
136          pop();
137        }
138        else {
139          if (!suppressIndent)
140            indent(level);
141          write("</");
142          write(pop());
143          write(">");
144        }
145        suppressIndent = false;
146      }
147    
148      private void indent(int level) {
149        writer.println();
150        for (int i = 0; i < level; i++)
151          write("  ");
152      }
153    
154      private void write(String str) {
155        writer.print(str);
156      }
157    
158      private void write(char c) {
159        writer.print(c);
160      }
161    
162      private void push(String s) {
163        tagStack.addElement(s);
164      }
165    
166      private String pop() {
167        String s = (String)tagStack.lastElement();
168        tagStack.setSize(tagStack.size() - 1);
169        return s;
170      }
171    
172      class DumpPatternVisitor implements PatternVisitor {
173        public void visitEmpty() {
174          startElement("empty");
175          endElement();
176        }
177    
178        public void visitNotAllowed() {
179          startElement("notAllowed");
180          endElement();
181        }
182    
183        public void visitError() {
184          startElement("error");
185          endElement();
186        }
187    
188        public void visitGroup(Pattern p1, Pattern p2) {
189          startElement("group");
190          p1.accept(groupPatternVisitor);
191          p2.accept(groupPatternVisitor);
192          endElement();
193        }
194    
195        public void visitInterleave(Pattern p1, Pattern p2) {
196          startElement("interleave");
197          p1.accept(interleavePatternVisitor);
198          p2.accept(interleavePatternVisitor);
199          endElement();
200        }
201    
202        public void visitChoice(Pattern p1, Pattern p2) {
203          startElement("choice");
204          p1.accept(choicePatternVisitor);
205          p2.accept(choicePatternVisitor);
206          endElement();
207        }
208    
209        public void visitOneOrMore(Pattern p) {
210          startElement("oneOrMore");
211          p.accept(groupPatternVisitor);
212          endElement();
213        }
214    
215        public void visitElement(NameClass nc, Pattern content) {
216          startElement("element");
217          nc.accept(nameClassVisitor);
218          startElement("ref");
219          attribute("name", getName(content));
220          endElement();
221          endElement();
222        }
223    
224        public void visitAttribute(NameClass nc, Pattern value) {
225          startElement("attribute");
226          nc.accept(nameClassVisitor);
227          value.accept(patternVisitor);
228          endElement();
229        }
230    
231        public void visitData(Datatype dt) {
232          startElement("text");     // XXX
233          endElement();
234        }
235    
236        public void visitDataExcept(Datatype dt, Pattern except) {
237          startElement("text");     // XXX
238          endElement();
239        }
240    
241        public void visitValue(Datatype dt, Object obj) {
242          startElement("value");
243          // XXX dump dt
244          // XXX toString will not handle QName
245          data(obj.toString());
246          endElement();
247        }
248    
249        public void visitText() {
250          startElement("text");
251          endElement();
252        }
253    
254        public void visitList(Pattern p) {
255          startElement("list");
256          p.accept(groupPatternVisitor);
257          endElement();
258        }
259      }
260    
261      class GroupDumpPatternVisitor extends DumpPatternVisitor {
262        public void visitGroup(Pattern p1, Pattern p2) {
263          p1.accept(this);
264          p2.accept(this);
265        }
266      }
267    
268      class ChoiceDumpPatternVisitor extends DumpPatternVisitor {
269        public void visitChoice(Pattern p1, Pattern p2) {
270          p1.accept(this);
271          p2.accept(this);
272        }
273      }
274    
275      class InterleaveDumpPatternVisitor extends DumpPatternVisitor {
276        public void visitInterleave(Pattern p1, Pattern p2) {
277          p1.accept(this);
278          p2.accept(this);
279        }
280      }
281    
282    
283      class DumpNameClassVisitor implements NameClassVisitor {
284        public void visitChoice(NameClass nc1, NameClass nc2) {
285          startElement("choice");
286          nc1.accept(choiceNameClassVisitor);
287          nc2.accept(choiceNameClassVisitor);
288          endElement();
289        }
290    
291        public void visitNsName(String ns) {
292          startElement("nsName");
293          attribute("ns", ns);
294          endElement();
295        }
296    
297        public void visitNsNameExcept(String ns, NameClass nc) {
298          startElement("nsName");
299          attribute("ns", ns);
300          startElement("except");
301          nc.accept(choiceNameClassVisitor);
302          endElement();
303          endElement();
304        }
305    
306        public void visitAnyName() {
307          startElement("anyName");
308          endElement();
309        }
310    
311        public void visitAnyNameExcept(NameClass nc) {
312          startElement("anyName");
313          startElement("except");
314          nc.accept(choiceNameClassVisitor);
315          endElement();
316          endElement();
317        }
318    
319        public void visitName(Name name) {
320          startElement("name");
321          attribute("ns", name.getNamespaceUri());
322          data(name.getLocalName());
323          endElement();
324        }
325    
326        public void visitError() {
327          startElement("error");
328          endElement();
329        }
330        
331        public void visitNull() {
332          visitAnyName();
333        }
334      }
335    
336      class ChoiceDumpNameClassVisitor extends DumpNameClassVisitor {
337        public void visitChoice(NameClass nc1, NameClass nc2) {
338          nc1.accept(this);
339          nc2.accept(this);
340        }
341      }
342    }