1 13 package org.apache.cocoon.generation; 14 15 import java.io.BufferedReader ; 16 import java.io.ByteArrayInputStream ; 17 import java.io.CharArrayWriter ; 18 import java.io.IOException ; 19 import java.io.InputStream ; 20 import java.io.InputStreamReader ; 21 import java.io.Reader ; 22 import java.io.Serializable ; 23 import java.util.HashMap ; 24 import java.util.Map ; 25 26 import org.apache.avalon.framework.parameters.Parameters; 27 import org.apache.cocoon.ProcessingException; 28 import org.apache.cocoon.environment.SourceResolver; 29 import org.apache.excalibur.source.Source; 30 import org.xml.sax.Attributes ; 31 import org.xml.sax.Locator ; 32 import org.xml.sax.SAXException ; 33 import org.xml.sax.helpers.AttributesImpl ; 34 35 97 public class CSVGenerator extends FileGenerator { 98 99 100 public static final String NAMESPACE_URI = "http://apache.org/cocoon/csv/1.0"; 101 102 public static final String NAMESPACE_PREFIX = "csv"; 103 104 105 private static final String DEFAULT_ENCODING = 106 new InputStreamReader (new ByteArrayInputStream (new byte[0])).getEncoding(); 107 108 private static final String DEFAULT_SEPARATOR = ","; 109 110 private static final String DEFAULT_ESCAPE = "\""; 111 112 private static final int DEFAULT_BUFFER_SIZE = 4096; 113 114 private static final char INDENT_STRING[] = "\n ".toCharArray(); 115 116 117 private String encoding = DEFAULT_ENCODING; 118 119 private char separator = DEFAULT_SEPARATOR.charAt(0); 120 121 private char escape = DEFAULT_ESCAPE.charAt(0); 122 123 private int buffersize = DEFAULT_BUFFER_SIZE; 124 125 private int fieldnumber = 1; 126 127 private int recordnumber = 1; 128 129 private boolean openrecord = false; 130 131 private CharArrayWriter buffer = null; 132 133 private Map columns = null; 134 135 138 public CSVGenerator() { 139 super(); 140 } 141 142 145 public void recycle() { 146 super.recycle(); 147 148 this.encoding = DEFAULT_ENCODING; 149 this.separator = DEFAULT_SEPARATOR.charAt(0); 150 this.escape = DEFAULT_ESCAPE.charAt(0); 151 this.buffersize = DEFAULT_BUFFER_SIZE; 152 this.buffer = null; 153 this.columns = null; 154 this.recordnumber = 1; 155 this.fieldnumber = 1; 156 this.openrecord = false; 157 } 158 159 162 public void setup(SourceResolver resolver, Map object_model, String source, 163 Parameters parameters) 164 throws ProcessingException, SAXException , IOException { 165 super.setup(resolver, object_model, source, parameters); 166 167 boolean header = parameters.getParameterAsBoolean("process-header", false); 168 169 this.encoding = parameters.getParameter("encoding", DEFAULT_ENCODING); 170 this.separator = parameters.getParameter("separator", DEFAULT_SEPARATOR).charAt(0); 171 this.escape = parameters.getParameter("escape", DEFAULT_ESCAPE).charAt(0); 172 this.buffersize = parameters.getParameterAsInteger("buffer-size", DEFAULT_BUFFER_SIZE); 173 this.buffer = new CharArrayWriter (); 174 this.columns = (header ? new HashMap () : null); 175 this.recordnumber = (header ? 0 : 1); 176 this.fieldnumber = 1; 177 this.openrecord = false; 178 } 179 180 183 public Serializable getKey() { 184 String key = this.inputSource.getURI(); 185 if (this.columns != null) return (key + "+headers"); 186 return key; 187 } 188 189 192 public void generate() 193 throws IOException , SAXException , ProcessingException { 194 195 196 CSVReader csv = new CSVReader(this.inputSource, this.encoding, this.buffersize); 197 198 try { 199 200 this.contentHandler.setDocumentLocator(csv); 201 this.contentHandler.startDocument(); 202 this.contentHandler.startPrefixMapping(NAMESPACE_PREFIX, NAMESPACE_URI); 203 this.indent(0); 204 this.startElement("document"); 205 206 207 boolean unescaped = true; 208 int prev = -1; 209 int curr = -1; 210 211 212 while ((curr = csv.read()) >= 0) { 213 214 215 if (curr == this.escape) { 216 if ((unescaped) && (prev == this.escape)) { 217 this.buffer.write(this.escape); 218 } 219 unescaped = ! unescaped; 220 prev = curr; 221 continue; 222 } 223 224 225 if ((unescaped) && (curr == this.separator)) { 226 this.dumpField(); 227 prev = curr; 228 continue; 229 } 230 231 232 if ((unescaped) && ((curr == '\r') || (curr == '\n'))) { 233 this.dumpField(); 234 this.dumpRecord(); 235 236 237 if (((curr == '\n') && (prev != '\r')) || (curr == '\r')) { 238 this.recordnumber ++; 239 } 240 241 242 prev = curr; 243 continue; 244 } 245 246 247 this.buffer.write(curr); 248 prev = curr; 249 } 250 251 252 this.dumpField(); 253 this.dumpRecord(); 254 255 256 this.indent(0); 257 this.endElement("document"); 258 this.contentHandler.endPrefixMapping(NAMESPACE_PREFIX); 259 this.contentHandler.endDocument(); 260 261 } finally { 262 csv.close(); 263 } 264 } 265 266 267 private void dumpField() 268 throws SAXException { 269 if (this.buffer.size() < 1) { 270 this.fieldnumber ++; 271 return; 272 } 273 274 if (! this.openrecord) { 275 this.indent(4); 276 277 if (this.recordnumber > 0) { 278 AttributesImpl attributes = new AttributesImpl (); 279 String value = Integer.toString(this.recordnumber); 280 attributes.addAttribute("", "number", "number", "CDATA", value); 281 this.startElement("record", attributes); 282 } else { 283 this.startElement("header"); 284 } 285 this.openrecord = true; 286 } 287 288 289 String element = "field"; 290 char array[] = this.buffer.toCharArray(); 291 this.indent(8); 292 293 AttributesImpl attributes = new AttributesImpl (); 294 String value = Integer.toString(this.fieldnumber); 295 attributes.addAttribute("", "number", "number", "CDATA", value); 296 297 if (this.recordnumber < 1) { 298 this.columns.put(new Integer (this.fieldnumber), new String (array)); 299 element = "column"; 300 } else if (this.columns != null) { 301 String header = (String ) this.columns.get(new Integer (this.fieldnumber)); 302 if (header != null) { 303 attributes.addAttribute("", "column", "column", "CDATA", header); 304 } 305 } 306 307 this.startElement(element, attributes); 308 this.contentHandler.characters(array, 0, array.length); 309 this.endElement(element); 310 this.buffer.reset(); 311 312 this.fieldnumber ++; 313 } 314 315 private void dumpRecord() 316 throws SAXException { 317 if (this.openrecord) { 318 this.indent(4); 319 if (this.recordnumber > 0) { 320 this.endElement("record"); 321 } else { 322 this.endElement("header"); 323 } 324 this.openrecord = false; 325 } 326 this.fieldnumber = 1; 327 } 328 329 private void indent(int level) 330 throws SAXException { 331 this.contentHandler.characters(INDENT_STRING, 0, level + 1); 332 } 333 334 private void startElement(String name) 335 throws SAXException { 336 this.startElement(name, new AttributesImpl ()); 337 } 338 339 private void startElement(String name, Attributes atts) 340 throws SAXException { 341 if (name == null) throw new NullPointerException ("Null name"); 342 if (atts == null) atts = new AttributesImpl (); 343 String qual = NAMESPACE_PREFIX + ':' + name; 344 this.contentHandler.startElement(NAMESPACE_URI, name, qual, atts); 345 } 346 347 private void endElement(String name) 348 throws SAXException { 349 String qual = NAMESPACE_PREFIX + ':' + name; 350 this.contentHandler.endElement(NAMESPACE_URI, name, qual); 351 } 352 353 private static final class CSVReader extends Reader implements Locator { 354 355 private String uri = null; 356 private Reader input = null; 357 private int column = 1; 358 private int line = 1; 359 private int last = -1; 360 361 private CSVReader(Source source, String encoding, int buffer) 362 throws IOException { 363 InputStream stream = source.getInputStream(); 364 Reader reader = new InputStreamReader (stream, encoding); 365 this.input = new BufferedReader (reader, buffer); 366 this.uri = source.getURI(); 367 } 368 369 public String getPublicId() { 370 return null; 371 } 372 373 public String getSystemId() { 374 return this.uri; 375 } 376 377 public int getLineNumber() { 378 return this.line; 379 } 380 381 public int getColumnNumber() { 382 return this.column; 383 } 384 385 public void close() 386 throws IOException { 387 this.input.close(); 388 } 389 390 public int read() 391 throws IOException { 392 int c = this.input.read(); 393 if (c < 0) return c; 394 395 if (((c == '\n') && (this.last != '\r')) || (c == '\r')) { 396 this.column = 1; 397 this.line ++; 398 } 399 400 this.last = c; 401 return c; 402 } 403 404 public int read(char b[], int o, int l) 405 throws IOException { 406 if (b == null) throw new NullPointerException (); 407 if ((o<0)||(o>b.length)||(l<0)||((o+l)>b.length)||((o+l)<0)) { 408 throw new IndexOutOfBoundsException (); 409 } 410 if (l == 0) return 0; 411 412 int c = read(); 413 if (c == -1) return -1; 414 b[o] = (char)c; 415 416 int i = 1; 417 try { 418 for (i = 1; i < l ; i++) { 419 c = read(); 420 if (c == -1) break; 421 if (b != null) b[o + i] = (char)c; 422 } 423 } catch (IOException ee) { 424 return i; 425 } 426 return i; 427 } 428 } 429 } 430 | Popular Tags |