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 }