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("&");
111 break;
112 case '<':
113 write("<");
114 break;
115 case '>':
116 write(">");
117 break;
118 case '"':
119 if (isAttribute) {
120 write(""");
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 }