1 15 package org.apache.tapestry.util.xml; 16 17 import java.io.IOException ; 18 import java.io.InputStream ; 19 import java.net.URL ; 20 import java.util.ArrayList ; 21 import java.util.HashMap ; 22 import java.util.List ; 23 import java.util.Map ; 24 25 import javax.xml.parsers.ParserConfigurationException ; 26 import javax.xml.parsers.SAXParser ; 27 import javax.xml.parsers.SAXParserFactory ; 28 29 import org.apache.commons.logging.Log; 30 import org.apache.commons.logging.LogFactory; 31 import org.apache.hivemind.ApplicationRuntimeException; 32 import org.apache.hivemind.HiveMind; 33 import org.apache.hivemind.Location; 34 import org.apache.hivemind.Resource; 35 import org.apache.hivemind.impl.LocationImpl; 36 import org.apache.tapestry.Tapestry; 37 import org.apache.tapestry.util.RegexpMatcher; 38 import org.xml.sax.Attributes ; 39 import org.xml.sax.InputSource ; 40 import org.xml.sax.Locator ; 41 import org.xml.sax.SAXException ; 42 import org.xml.sax.SAXParseException ; 43 import org.xml.sax.helpers.DefaultHandler ; 44 45 63 64 public class RuleDirectedParser extends DefaultHandler 65 { 66 private static final Log LOG = LogFactory.getLog(RuleDirectedParser.class); 67 68 private Resource _documentLocation; 69 70 private List _ruleStack = new ArrayList (); 71 72 private List _objectStack = new ArrayList (); 73 74 private Object _documentObject; 75 76 private Locator _locator; 77 78 private int _line = -1; 79 80 private int _column = -1; 81 82 private Location _location; 83 84 private static SAXParserFactory _parserFactory; 85 86 private SAXParser _parser; 87 88 private RegexpMatcher _matcher; 89 90 private String _uri; 91 92 private String _localName; 93 94 private String _qName; 95 96 99 private Map _ruleMap = new HashMap (); 100 101 105 106 private StringBuffer _contentBuffer = new StringBuffer (); 107 108 111 112 private Map _entities = new HashMap (); 113 114 public Object parse(Resource documentLocation) 115 { 116 if (LOG.isDebugEnabled()) 117 LOG.debug("Parsing: " + documentLocation); 118 119 try 120 { 121 _documentLocation = documentLocation; 122 123 URL url = documentLocation.getResourceURL(); 124 125 if (url == null) 126 throw new DocumentParseException(Tapestry.format( 127 "RuleDrivenParser.resource-missing", 128 documentLocation), documentLocation, null); 129 130 return parse(url); 131 } 132 finally 133 { 134 _documentLocation = null; 135 _ruleStack.clear(); 136 _objectStack.clear(); 137 _documentObject = null; 138 139 _uri = null; 140 _localName = null; 141 _qName = null; 142 143 _line = -1; 144 _column = -1; 145 _location = null; 146 _locator = null; 147 148 _contentBuffer.setLength(0); 149 } 150 } 151 152 protected Object parse(URL url) 153 { 154 if (_parser == null) 155 _parser = constructParser(); 156 157 InputStream stream = null; 158 159 try 160 { 161 stream = url.openStream(); 162 } 163 catch (IOException ex) 164 { 165 throw new DocumentParseException(Tapestry.format( 166 "RuleDrivenParser.unable-to-open-resource", 167 url), _documentLocation, ex); 168 } 169 170 InputSource source = new InputSource (stream); 171 172 try 173 { 174 _parser.parse(source, this); 175 176 stream.close(); 177 } 178 catch (Exception ex) 179 { 180 throw new DocumentParseException(Tapestry.format( 181 "RuleDrivenParser.parse-error", 182 url, 183 ex.getMessage()), getLocation(), ex); 184 } 185 186 if (LOG.isDebugEnabled()) 187 LOG.debug("Document parsed as: " + _documentObject); 188 189 return _documentObject; 190 } 191 192 196 197 public Location getLocation() 198 { 199 if (_locator == null) 200 return null; 201 202 int line = _locator.getLineNumber(); 203 int column = _locator.getColumnNumber(); 204 205 if (_line != line || _column != column) 206 { 207 _location = null; 208 _line = line; 209 _column = column; 210 } 211 212 if (_location == null) 213 _location = new LocationImpl(_documentLocation, _line, _column); 214 215 return _location; 216 } 217 218 222 public void push(Object object) 223 { 224 if (_documentObject == null) 225 _documentObject = object; 226 227 push(_objectStack, object, "object stack"); 228 } 229 230 233 public Object peek() 234 { 235 return peek(_objectStack, 0); 236 } 237 238 242 243 public Object peek(int depth) 244 { 245 return peek(_objectStack, depth); 246 } 247 248 251 public Object pop() 252 { 253 return pop(_objectStack, "object stack"); 254 } 255 256 private Object pop(List list, String name) 257 { 258 Object result = list.remove(list.size() - 1); 259 260 if (LOG.isDebugEnabled()) 261 LOG.debug("Popped " + result + " off " + name + " (at " + getLocation() + ")"); 262 263 return result; 264 } 265 266 private Object peek(List list, int depth) 267 { 268 return list.get(list.size() - 1 - depth); 269 } 270 271 private void push(List list, Object object, String name) 272 { 273 if (LOG.isDebugEnabled()) 274 LOG.debug("Pushing " + object + " onto " + name + " (at " + getLocation() + ")"); 275 276 list.add(object); 277 } 278 279 282 283 protected void pushRule(IRule rule) 284 { 285 push(_ruleStack, rule, "rule stack"); 286 } 287 288 291 292 protected IRule peekRule() 293 { 294 return (IRule) peek(_ruleStack, 0); 295 } 296 297 protected IRule popRule() 298 { 299 return (IRule) pop(_ruleStack, "rule stack"); 300 } 301 302 public void addRule(String localElementName, IRule rule) 303 { 304 _ruleMap.put(localElementName, rule); 305 } 306 307 319 320 public void registerEntity(String publicId, String entityPath) 321 { 322 if (LOG.isDebugEnabled()) 323 LOG.debug("Registering " + publicId + " as " + entityPath); 324 325 if (_entities == null) 326 _entities = new HashMap (); 327 328 _entities.put(publicId, entityPath); 329 } 330 331 protected IRule selectRule(String localName, Attributes attributes) 332 { 333 IRule rule = (IRule) _ruleMap.get(localName); 334 335 if (rule == null) 336 throw new DocumentParseException(Tapestry.format( 337 "RuleDrivenParser.no-rule-for-element", 338 localName), getLocation(), null); 339 340 return rule; 341 } 342 343 350 public void setDocumentLocator(Locator locator) 351 { 352 _locator = locator; 353 } 354 355 359 public void characters(char[] ch, int start, int length) throws SAXException 360 { 361 _contentBuffer.append(ch, start, length); 362 } 363 364 367 public void endElement(String uri, String localName, String qName) throws SAXException 368 { 369 fireContentRule(); 370 371 _uri = uri; 372 _localName = localName; 373 _qName = qName; 374 375 popRule().endElement(this); 376 } 377 378 381 public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException 382 { 383 } 384 385 389 public void startElement(String uri, String localName, String qName, Attributes attributes) 390 throws SAXException 391 { 392 fireContentRule(); 393 394 _uri = uri; 395 _localName = localName; 396 _qName = qName; 397 398 String name = extractName(uri, localName, qName); 399 400 IRule newRule = selectRule(name, attributes); 401 402 pushRule(newRule); 403 404 newRule.startElement(this, attributes); 405 } 406 407 private String extractName(String uri, String localName, String qName) 408 { 409 return HiveMind.isBlank(localName) ? qName : localName; 410 } 411 412 416 protected synchronized SAXParser constructParser() 417 { 418 if (_parserFactory == null) 419 { 420 _parserFactory = SAXParserFactory.newInstance(); 421 configureParserFactory(_parserFactory); 422 } 423 424 try 425 { 426 return _parserFactory.newSAXParser(); 427 } 428 catch (SAXException ex) 429 { 430 throw new ApplicationRuntimeException(ex); 431 } 432 catch (ParserConfigurationException ex) 433 { 434 throw new ApplicationRuntimeException(ex); 435 } 436 437 } 438 439 443 444 protected void configureParserFactory(SAXParserFactory factory) 445 { 446 factory.setValidating(true); 447 factory.setNamespaceAware(false); 448 } 449 450 453 public void error(SAXParseException ex) throws SAXException 454 { 455 fatalError(ex); 456 } 457 458 461 public void fatalError(SAXParseException ex) throws SAXException 462 { 463 467 _parser = null; 468 469 throw ex; 470 } 471 472 475 public void warning(SAXParseException ex) throws SAXException 476 { 477 fatalError(ex); 478 } 479 480 public InputSource resolveEntity(String publicId, String systemId) throws SAXException 481 { 482 String entityPath = null; 483 484 if (LOG.isDebugEnabled()) 485 LOG.debug("Attempting to resolve entity; publicId = " + publicId + " systemId = " 486 + systemId); 487 488 if (_entities != null) 489 entityPath = (String ) _entities.get(publicId); 490 491 if (entityPath == null) 492 { 493 if (LOG.isDebugEnabled()) 494 LOG.debug("Entity not found, using " + systemId); 495 496 return null; 497 } 498 499 InputStream stream = getClass().getResourceAsStream(entityPath); 500 501 InputSource result = new InputSource (stream); 502 503 if (result != null && LOG.isDebugEnabled()) 504 LOG.debug("Resolved " + publicId + " as " + result + " (for " + entityPath + ")"); 505 506 return result; 507 } 508 509 514 515 public void validate(String value, String pattern, String errorKey) 516 throws DocumentParseException 517 { 518 if (_matcher == null) 519 _matcher = new RegexpMatcher(); 520 521 if (_matcher.matches(pattern, value)) 522 return; 523 524 throw new InvalidStringException(Tapestry.format(errorKey, value), value, getLocation()); 525 } 526 527 public Resource getDocumentLocation() 528 { 529 return _documentLocation; 530 } 531 532 538 public String getLocalName() 539 { 540 return _localName; 541 } 542 543 549 public String getQName() 550 { 551 return _qName; 552 } 553 554 560 public String getUri() 561 { 562 return _uri; 563 } 564 565 private void fireContentRule() 566 { 567 String content = _contentBuffer.toString(); 568 _contentBuffer.setLength(0); 569 570 if (!_ruleStack.isEmpty()) 571 peekRule().content(this, content); 572 } 573 574 } | Popular Tags |