001 /* 002 // $Id: //open/mondrian/src/main/mondrian/xmla/impl/DefaultSaxWriter.java#7 $ 003 // This software is subject to the terms of the Common Public License 004 // Agreement, available at the following URL: 005 // http://www.opensource.org/licenses/cpl.html. 006 // Copyright (C) 2005-2008 Julian Hyde 007 // All Rights Reserved. 008 // You must accept the terms of that agreement to use this software. 009 */ 010 package mondrian.xmla.impl; 011 012 import java.io.OutputStream; 013 import java.io.OutputStreamWriter; 014 import java.io.PrintWriter; 015 import java.io.UnsupportedEncodingException; 016 import java.io.Writer; 017 import java.util.Stack; 018 019 import mondrian.xmla.SaxWriter; 020 021 import org.eigenbase.xom.XMLUtil; 022 import org.eigenbase.xom.XOMUtil; 023 import org.xml.sax.Attributes; 024 025 /** 026 * Default implementation of {@link SaxWriter}. 027 * 028 * @author jhyde 029 * @author Gang Chen 030 * @since 27 April, 2003 031 */ 032 public class DefaultSaxWriter implements SaxWriter { 033 /** Inside the tag of an element. */ 034 private static final int STATE_IN_TAG = 0; 035 /** After the tag at the end of an element. */ 036 private static final int STATE_END_ELEMENT = 1; 037 /** After the tag at the start of an element. */ 038 private static final int STATE_AFTER_TAG = 2; 039 /** After a burst of character data. */ 040 private static final int STATE_CHARACTERS = 3; 041 042 private final PrintWriter writer; 043 private int indent; 044 private String indentStr = " "; 045 private final Stack stack = new Stack(); 046 private int state = STATE_END_ELEMENT; 047 048 049 /** 050 * Creates a <code>SAXWriter</code> writing to an {@link java.io.OutputStream}. 051 */ 052 public DefaultSaxWriter(OutputStream stream) { 053 this(new OutputStreamWriter(stream)); 054 } 055 public DefaultSaxWriter(OutputStream stream, String xmlEncoding) 056 throws UnsupportedEncodingException { 057 this(new OutputStreamWriter(stream, xmlEncoding)); 058 } 059 060 /** 061 * Creates a <code>SAXWriter</code> writing to a {@link java.io.Writer}. 062 * 063 * <p>If <code>writer</code> is a {@link java.io.PrintWriter}, 064 * {@link #DefaultSaxWriter(java.io.OutputStream)} is preferred. 065 */ 066 public DefaultSaxWriter(Writer writer) { 067 this(new PrintWriter(writer), 0); 068 } 069 070 /** 071 * Creates a <code>SAXWriter</code> writing to a {@link java.io.PrintWriter}. 072 * @param writer 073 * @param initialIndent 074 */ 075 public DefaultSaxWriter(PrintWriter writer, int initialIndent) { 076 this.writer = writer; 077 this.indent = initialIndent; 078 } 079 080 private void _startElement( 081 String namespaceURI, String localName, 082 String qName, Attributes atts) { 083 _checkTag(); 084 if (indent > 0) { 085 writer.println(); 086 } 087 for (int i = 0; i < indent; i++) { 088 writer.write(indentStr); 089 } 090 indent++; 091 writer.write('<'); 092 writer.write(qName); 093 for (int i = 0; i < atts.getLength(); i++) { 094 XMLUtil.printAtt(writer, atts.getQName(i), atts.getValue(i)); 095 } 096 state = STATE_IN_TAG; 097 } 098 099 private void _checkTag() { 100 if (state == STATE_IN_TAG) { 101 state = STATE_AFTER_TAG; 102 writer.print(">"); 103 } 104 } 105 106 private void _endElement( 107 String namespaceURI, String localName, String qName) { 108 indent--; 109 if (state == STATE_IN_TAG) { 110 writer.write("/>"); 111 } else { 112 if (state != STATE_CHARACTERS) { 113 writer.println(); 114 for (int i = 0; i < indent; i++) { 115 writer.write(indentStr); 116 } 117 } 118 writer.write("</"); 119 writer.write(qName); 120 writer.write('>'); 121 } 122 state = STATE_END_ELEMENT; 123 } 124 125 private void _characters(char ch[], int start, int length) { 126 _checkTag(); 127 128 // Display the string, quoting in <![CDATA[ ... ]]> if necessary, 129 // or using XML escapes as a last result. 130 String s = new String(ch, start, length); 131 if (XOMUtil.stringHasXMLSpecials(s)) { 132 XMLUtil.stringEncodeXML(s, writer); 133 /* 134 if (s.indexOf("]]>") < 0) { 135 writer.print("<![CDATA["); 136 writer.print(s); 137 writer.print("]]>"); 138 } else { 139 XMLUtil.stringEncodeXML(s, writer); 140 } 141 */ 142 } else { 143 writer.print(s); 144 } 145 146 state = STATE_CHARACTERS; 147 } 148 149 150 // 151 // Simplifying methods 152 153 public void characters(String s) { 154 if (s != null && s.length() > 0) { 155 _characters(s.toCharArray(), 0, s.length()); 156 } 157 } 158 159 public void element(String tagName, String[] attributes) { 160 startElement(tagName, attributes); 161 endElement(); 162 } 163 164 public void startElement(String tagName) { 165 _startElement(null, null, tagName, EmptyAttributes); 166 stack.push(tagName); 167 } 168 169 public void startElement(String tagName, String[] attributes) { 170 _startElement(null, null, tagName, new StringAttributes(attributes)); 171 stack.push(tagName); 172 } 173 174 public void endElement() { 175 String tagName = (String) stack.pop(); 176 _endElement(null, null, tagName); 177 } 178 179 public void startDocument() { 180 if (stack.size() != 0) { 181 throw new IllegalStateException("Document already started"); 182 } 183 } 184 185 public void endDocument() { 186 if (stack.size() != 0) { 187 throw new IllegalStateException("Document may have unbalanced elements"); 188 } 189 writer.flush(); 190 } 191 192 public void completeBeforeElement(String tagName) { 193 if (stack.indexOf(tagName) == -1) { 194 return; 195 } 196 197 String currentTagName = (String) stack.peek(); 198 while (!tagName.equals(currentTagName)) { 199 _endElement(null, null, currentTagName); 200 stack.pop(); 201 currentTagName = (String) stack.peek(); 202 } 203 } 204 205 public void verbatim(String text) { 206 _checkTag(); 207 writer.print(text); 208 } 209 210 public void flush() { 211 writer.flush(); 212 } 213 214 static private final Attributes EmptyAttributes = new Attributes() { 215 public int getLength() { 216 return 0; 217 } 218 219 public String getURI(int index) { 220 return null; 221 } 222 223 public String getLocalName(int index) { 224 return null; 225 } 226 227 public String getQName(int index) { 228 return null; 229 } 230 231 public String getType(int index) { 232 return null; 233 } 234 235 public String getValue(int index) { 236 return null; 237 } 238 239 public int getIndex(String uri, String localName) { 240 return 0; 241 } 242 243 public int getIndex(String qName) { 244 return 0; 245 } 246 247 public String getType(String uri, String localName) { 248 return null; 249 } 250 251 public String getType(String qName) { 252 return null; 253 } 254 255 public String getValue(String uri, String localName) { 256 return null; 257 } 258 259 public String getValue(String qName) { 260 return null; 261 } 262 }; 263 264 /** 265 * List of SAX attributes based upon a string array. 266 */ 267 public static class StringAttributes implements Attributes { 268 private final String[] strings; 269 270 public StringAttributes(String[] strings) { 271 this.strings = strings; 272 } 273 274 public int getLength() { 275 return strings.length / 2; 276 } 277 278 public String getURI(int index) { 279 return null; 280 } 281 282 public String getLocalName(int index) { 283 return null; 284 } 285 286 public String getQName(int index) { 287 return strings[index * 2]; 288 } 289 290 public String getType(int index) { 291 return null; 292 } 293 294 public String getValue(int index) { 295 return strings[index * 2 + 1]; 296 } 297 298 public int getIndex(String uri, String localName) { 299 return -1; 300 } 301 302 public int getIndex(String qName) { 303 final int count = strings.length / 2; 304 for (int i = 0; i < count; i++) { 305 String string = strings[i * 2]; 306 if (string.equals(qName)) { 307 return i; 308 } 309 } 310 return -1; 311 } 312 313 public String getType(String uri, String localName) { 314 return null; 315 } 316 317 public String getType(String qName) { 318 return null; 319 } 320 321 public String getValue(String uri, String localName) { 322 return null; 323 } 324 325 public String getValue(String qName) { 326 final int index = getIndex(qName); 327 if (index < 0) { 328 return null; 329 } else { 330 return strings[index * 2 + 1]; 331 } 332 } 333 } 334 } 335 336 // End DefaultSaxWriter.java