/******************************************************************************* * Copyright (c) 2012 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is made available under the terms of the * Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.cdi.seam.config.core.xml; import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.jboss.tools.common.CommonPlugin; import org.jboss.tools.common.core.Messages; import org.jboss.tools.common.xml.SAXValidator; import org.jboss.tools.common.xml.XMLEntityResolverImpl; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.SAXNotRecognizedException; import org.xml.sax.SAXNotSupportedException; import org.xml.sax.SAXParseException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; /** * * @author Viacheslav Kabanovich * */ public class SAXParser extends SAXValidator { /** * If this limit is set to 0, parser will abort on the very first fatal error. * In that case, the completing of parsing process is guaranteed, though parser * will not be so smart as to extract maximum of correct data for the model. * * If this limit is set to a positive number, parser will be smart, but is not * secured against making a mistake. * */ int supperssedFatalErrorLimit = 0; public void setSupperssedFatalErrorLimit(int c) { supperssedFatalErrorLimit = c; } /** * * @param handler * @return instanceof XMLReader or null if Apache SAX parser is not available. */ XMLReader createParser1(DefaultHandler handler) { XMLReader parserInstance = null; //XMLReaderFactory.createXMLReader(DEFAULT_SAX_PARSER_CLASS_NAME); parserInstance = new org.apache.xerces.parsers.SAXParser(); setFeature(parserInstance, NAMESPACES_FEATURE_ID, true); setFeature(parserInstance, NAMESPACE_PREFIXES_FEATURE_ID, false); setFeature(parserInstance, VALIDATION_FEATURE_ID, true); setFeature(parserInstance, VALIDATION_SCHEMA_FEATURE_ID, true); setFeature(parserInstance, VALIDATION_SCHEMA_CHECKING_FEATURE_ID, false); setFeature(parserInstance, VALIDATION_DYNAMIC_FEATURE_ID, false); setFeature(parserInstance, FATAL_ERROR_PROCESSING_FEATURE_ID, supperssedFatalErrorLimit > 0); try { parserInstance.setProperty(ENTITY_RESOLVER_PROPERTY_ID, new XMLEntityResolverImpl()); } catch (SAXNotRecognizedException e1) { CommonPlugin.getPluginLog().logError( e1.getMessage()+"", e1); //$NON-NLS-1$ } catch (SAXNotSupportedException e1) { CommonPlugin.getPluginLog().logError( e1.getMessage()+"", e1); //$NON-NLS-1$ } parserInstance.setContentHandler(handler); parserInstance.setErrorHandler(handler); return parserInstance; } private String errorMessage = null; List<String> errors = new ArrayList<String>(); public SAXElement parse(InputStream input, IDocument document) { InputSource s = new InputSource(input); ConfigHanlder handler = new ConfigHanlder(document); XMLReader reader = createParser1(handler); try { if(reader != null) { reader.parse(s); errorMessage = null; } else if(errorMessage == null) { //Report only the first failure. errorMessage = MessageFormat.format( Messages.SAXValidator_UnableToInstantiateMessage, DEFAULT_SAX_PARSER_CLASS_NAME); CommonPlugin.getDefault().logError(errorMessage); } } catch (IOException e) { CommonPlugin.getDefault().logError(e); } catch (SAXException e) { //ignore, that is user data error that will be shown as error marker. } errors = handler.errors; return handler.getRootElement(); } public List<String> getErrors() { return errors; } class ConfigHanlder extends DefaultHandler { SAXElement root = null; IDocument document; Locator locator; List<String> errors = new ArrayList<String>(); SAXElement current = null; StringBuffer currentText = new StringBuffer(); Location currentTextLocation = null; ConfigHanlder(IDocument document) { this.document = document; } public SAXElement getRootElement() { return root; } public void setDocumentLocator (Locator locator) { this.locator = locator; } private int getCurrentLocation() { if (locator != null) { int line = locator.getLineNumber() - 1; int lineOffset = locator.getColumnNumber() - 1; try { return document.getLineOffset(line) + lineOffset; } catch (BadLocationException e) { CommonPlugin.getDefault().logError(e); } } return 0; } public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { int end = getCurrentLocation(), start = 0; if(end > 0) { start = document.get().lastIndexOf("<", end - 1); } SAXElement element = new SAXElement(); element.setLocalName(localName); element.setURI(uri); element.setName(qName); element.setParent(current); if(start >= 0) { element.setLocation(new Location(start, end - start, getLine(start))); int ns = document.get().indexOf(qName, start); if(ns >= start) { element.setNameLocation(new Location(ns, qName.length(), getLine(ns))); } } for (int i = 0; i < attributes.getLength(); i++) { String n = attributes.getLocalName(i); String v = attributes.getValue(i); SAXAttribute a = new SAXAttribute(); a.setName(n); a.setValue(v); int n_start = document.get().indexOf(n, start); if(n_start >= 0) { a.setNameLocation(new Location(n_start, n.length(), getLine(n_start))); int v_start = document.get().indexOf('"', n_start); if(v_start >= 0) { a.setValueLocation(new Location(v_start + 1, v.length(), getLine(v_start))); } } //TODO element.addAttribute(a); } current = element; currentText = new StringBuffer(); currentTextLocation = null; if(root == null) root = element; } public void characters (char ch[], int start, int length) throws SAXException { String append = new String(ch, start, length); if(append.trim().length() == 0) return; int end = getCurrentLocation(), s = 0; if (end > 0) { s = end - length; } currentText.append(append); currentTextLocation = new Location(s, length, getLine(s)); } public void endElement(String uri, String localName, String qName) throws SAXException { int end = getCurrentLocation(); if(end > 0) { int start = document.get().lastIndexOf("<", end); Location endLocation = new Location(start, end - start, getLine(start)); //TODO } if(currentText.length() > 0) { SAXText text = new SAXText(); text.setValue(currentText.toString()); current.setTextNode(text); text.setLocation(currentTextLocation); currentText.setLength(0); } if(current.getParent() != null) { current.getParent().addChildElement(current); } current = current.getParent(); } public void error(SAXParseException e) throws SAXException { String message = e.getMessage(); errors.add(message); } public void fatalError(SAXParseException e) throws SAXException { String message = e.getMessage(); errors.add(message); if(errors.size() > supperssedFatalErrorLimit) throw e; } /** * Returns line number in text for offset 'start'; the first line has number 1. * * @param start * @return */ private int getLine(int start) { try { return document.getLineOfOffset(start) + 1; } catch (BadLocationException e) { CommonPlugin.getPluginLog().logError(e); return -1; } } } }