/** * * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * **/ package lucee.runtime.util; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.ErrorListener; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import lucee.commons.io.res.Resource; import lucee.loader.engine.CFMLEngine; import lucee.loader.engine.CFMLEngineFactory; import lucee.loader.util.Util; import lucee.runtime.PageContext; import lucee.runtime.exp.PageException; import lucee.runtime.type.Struct; import org.apache.xalan.processor.TransformerFactoryImpl; import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl; import org.ccil.cowan.tagsoup.Parser; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; /** * */ public final class XMLUtilImpl implements XMLUtil { public final static String NON_VALIDATING_DTD_GRAMMAR="http://apache.org/xml/features/nonvalidating/load-dtd-grammar"; public final static String NON_VALIDATING_DTD_EXTERNAL="http://apache.org/xml/features/nonvalidating/load-external-dtd"; public final static String VALIDATION_SCHEMA="http://apache.org/xml/features/validation/schema"; public final static String VALIDATION_SCHEMA_FULL_CHECKING="http://apache.org/xml/features/validation/schema-full-checking"; public static final short UNDEFINED_NODE=-1; /* public static final Collection.Key XMLCOMMENT = KeyImpl.intern("xmlcomment"); public static final Collection.Key XMLTEXT = KeyImpl.intern("xmltext"); public static final Collection.Key XMLCDATA = KeyImpl.intern("xmlcdata"); public static final Collection.Key XMLCHILDREN = KeyImpl.intern("xmlchildren"); public static final Collection.Key XMLNODES = KeyImpl.intern("xmlnodes"); public static final Collection.Key XMLNSURI = KeyImpl.intern("xmlnsuri"); public static final Collection.Key XMLNSPREFIX = KeyImpl.intern("xmlnsprefix"); public static final Collection.Key XMLROOT = KeyImpl.intern("xmlroot"); public static final Collection.Key XMLPARENT = KeyImpl.intern("xmlparent"); public static final Collection.Key XMLNAME = KeyImpl.intern("xmlname"); public static final Collection.Key XMLTYPE = KeyImpl.intern("xmltype"); public static final Collection.Key XMLVALUE = KeyImpl.intern("xmlvalue"); public static final Collection.Key XMLATTRIBUTES = KeyImpl.intern("xmlattributes"); */ //static DOMParser parser = new DOMParser(); private static DocumentBuilder docBuilder; //private static DocumentBuilderFactory factory; private static TransformerFactory transformerFactory; @Override public String unescapeXMLString(String str) { StringBuilder rtn=new StringBuilder(); int posStart=-1; int posFinish=-1; while((posStart=str.indexOf('&',posStart))!=-1) { int last=posFinish+1; posFinish=str.indexOf(';',posStart); if(posFinish==-1)break; rtn.append(str.substring(last,posStart)); if(posStart+1<posFinish) { rtn.append(unescapeXMLEntity(str.substring(posStart+1,posFinish))); } else { rtn.append("&;"); } posStart=posFinish+1; } rtn.append(str.substring(posFinish+1)); return rtn.toString(); } /* public String unescapeXMLString2(String str) { StringBuffer sb=new StringBuffer(); int index,last=0,indexSemi; while((index=str.indexOf('&',last))!=-1) { sb.append(str.substring(last,index)); indexSemi=str.indexOf(';',index+1); if(indexSemi==-1) { sb.append('&'); last=index+1; } else if(index+1==indexSemi) { sb.append("&;"); last=index+2; } else { sb.append(unescapeXMLEntity(str.substring(index+1,indexSemi))); last=indexSemi+1; } } sb.append(str.substring(last)); return sb.toString(); }*/ private static String unescapeXMLEntity(String str) { if("lt".equals(str)) return "<"; if("gt".equals(str)) return ">"; if("amp".equals(str)) return "&"; if("apos".equals(str)) return "'"; if("quot".equals(str)) return "\""; return "&"+str+";"; } @Override public String escapeXMLString(String xmlStr) { char c; StringBuffer sb=new StringBuffer(); int len=xmlStr.length(); for(int i=0;i<len;i++) { c=xmlStr.charAt(i); if(c=='<') sb.append("<"); else if(c=='>') sb.append(">"); else if(c=='&') sb.append("&"); //else if(c=='\'') sb.append("&"); else if(c=='"') sb.append("""); //else if(c>127) sb.append("&#"+((int)c)+";"); else sb.append(c); } return sb.toString(); } @Override public TransformerFactory getTransformerFactory() { if(transformerFactory==null)transformerFactory=new TransformerFactoryImpl(); return transformerFactory; } /** * parse XML/HTML String to a XML DOM representation * @param xml XML InputSource * @param isHtml is a HTML or XML Object * @return parsed Document * @throws SAXException * @throws IOException */ public final Document parse(InputSource xml,InputSource validator, boolean isHtml) throws SAXException, IOException { if(!isHtml) { // try to load org.apache.xerces.jaxp.DocumentBuilderFactoryImpl, oracle impl sucks DocumentBuilderFactory factory = newDocumentBuilderFactory(); //print.o(factory); if(validator==null) { setAttributeEL(factory,NON_VALIDATING_DTD_EXTERNAL, Boolean.FALSE); setAttributeEL(factory,NON_VALIDATING_DTD_GRAMMAR, Boolean.FALSE); } else { setAttributeEL(factory,VALIDATION_SCHEMA, Boolean.TRUE); setAttributeEL(factory,VALIDATION_SCHEMA_FULL_CHECKING, Boolean.TRUE); } factory.setNamespaceAware(true); factory.setValidating(validator!=null); try { DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(new XMLEntityResolverDefaultHandler(validator)); builder.setErrorHandler(new ThrowingErrorHandler(true,true,false)); return builder.parse(xml); } catch (ParserConfigurationException e) { throw new SAXException(e); } /*DOMParser parser = new DOMParser(); print.out("parse"); parser.setEntityResolver(new XMLEntityResolverDefaultHandler(validator)); parser.parse(xml); return parser.getDocument();*/ } XMLReader reader = new Parser(); reader.setFeature(Parser.namespacesFeature, true); reader.setFeature(Parser.namespacePrefixesFeature, true); try { Transformer transformer = TransformerFactory.newInstance().newTransformer(); DOMResult result = new DOMResult(); transformer.transform(new SAXSource(reader, xml), result); return getDocument(result.getNode()); } catch (Exception e) { throw new SAXException(e); } } private DocumentBuilderFactory newDocumentBuilderFactory() { try{ return new DocumentBuilderFactoryImpl(); } catch(Throwable t) { return DocumentBuilderFactory.newInstance(); } } private static void setAttributeEL(DocumentBuilderFactory factory,String name, Object value) { try{ factory.setAttribute(name, value); } catch(Throwable t){ if(t instanceof ThreadDeath) throw (ThreadDeath)t; //SystemOut.printDate("attribute ["+name+"] is not allowed for ["+factory.getClass().getName()+"]"); } } @Override public void replaceChild(Node newChild, Node oldChild) { Node nc = newChild; Node oc = oldChild; Node p = oc.getParentNode(); if(nc!=oc)p.replaceChild(nc, oc); } @Override public boolean nameEqual(Node node, String name) { if(name==null) return false; return name.equals(node.getNodeName()) || name.equals(node.getLocalName()); } @Override public Element getRootElement(Node node) { Document doc=null; if(node instanceof Document) doc=(Document) node; else doc=node.getOwnerDocument(); return doc.getDocumentElement(); } @Override public Document newDocument() throws ParserConfigurationException, FactoryConfigurationError { if(docBuilder==null) { docBuilder=newDocumentBuilderFactory().newDocumentBuilder(); } return docBuilder.newDocument(); } @Override public Document getDocument(NodeList nodeList) throws PageException { if(nodeList instanceof Document) return (Document)nodeList; int len=nodeList.getLength(); for(int i=0;i<len;i++) { Node node=nodeList.item(i); if(node!=null) return node.getOwnerDocument(); } throw CFMLEngineFactory.getInstance().getExceptionUtil().createXMLException("can't get Document from NodeList, in NoteList are no Nodes"); } @Override public Document getDocument(Node node) { if(node instanceof Document) return (Document)node; return node.getOwnerDocument(); } @Override public synchronized ArrayList<Node> getChildNodes(Node node, short type, String filter) { ArrayList<Node> rtn=new ArrayList<Node>(); NodeList nodes=node.getChildNodes(); int len=nodes.getLength(); Node n; for(int i=0;i<len;i++) { try { n=nodes.item(i); if(n!=null && (type==UNDEFINED_NODE || n.getNodeType()==type)){ if(filter==null || filter.equals(n.getLocalName())) rtn.add(n); } } catch(Throwable t) {if(t instanceof ThreadDeath) throw (ThreadDeath)t;} } return rtn; } public synchronized List<Node> getChildNodesAsList(Node node, short type, String filter) { List<Node> rtn=new ArrayList<Node>(); NodeList nodes=node.getChildNodes(); int len=nodes.getLength(); Node n; for(int i=0;i<len;i++) { try { n=nodes.item(i); if(n!=null && (n.getNodeType()==type|| type==UNDEFINED_NODE)){ if(filter==null || filter.equals(n.getLocalName())) rtn.add(n); } } catch(Throwable t) {if(t instanceof ThreadDeath) throw (ThreadDeath)t;} } return rtn; } @Override public synchronized Node getChildNode(Node node, short type, String filter, int index) { NodeList nodes=node.getChildNodes(); int len=nodes.getLength(); Node n; int count=0; for(int i=0;i<len;i++) { try { n=nodes.item(i); if(n!=null && (type==UNDEFINED_NODE || n.getNodeType()==type)){ if(filter==null || filter.equals(n.getLocalName())) { if(count==index) return n; count++; } } } catch(Throwable t) {if(t instanceof ThreadDeath) throw (ThreadDeath)t;} } return null; } /** * return all Children of a node by a defined type as Node Array * @param node node to get children from * @param type type of returned node * @return all matching child node */ public Node[] getChildNodesAsArray(Node node, short type) { ArrayList<Node> nodeList=getChildNodes(node, type,null); return nodeList.toArray(new Node[nodeList.size()]); } public Node[] getChildNodesAsArray(Node node, short type, String filter) { ArrayList<Node> nodeList=getChildNodes(node, type,filter); return nodeList.toArray(new Node[nodeList.size()]); } /** * return all Element Children of a node * @param node node to get children from * @return all matching child node */ public Element[] getChildElementsAsArray(Node node) { ArrayList<Node> nodeList=getChildNodes(node,Node.ELEMENT_NODE,null); return nodeList.toArray(new Element[nodeList.size()]); } @Override public String transform(InputSource xml, InputSource xsl, Map<String,Object> parameters) throws TransformerException, SAXException, IOException { return transform( parse( xml, null , false ), xsl, parameters ); } @Override public String transform(Document doc, InputSource xsl, Map<String,Object> parameters) throws TransformerException { StringWriter sw = new StringWriter(); TransformerFactory factory = getTransformerFactory(); factory.setErrorListener(SimpleErrorListener.THROW_FATAL); Transformer transformer = factory.newTransformer(new StreamSource(xsl.getCharacterStream())); if (parameters != null) { Iterator it = parameters.entrySet().iterator(); while ( it.hasNext() ) { Map.Entry e = (Map.Entry) it.next(); transformer.setParameter(e.getKey().toString(), e.getValue()); } } transformer.transform(new DOMSource(doc), new StreamResult(sw)); return sw.toString(); } /** * returns the Node Type As String * @param node * @param cftype * @return */ public String getTypeAsString(Node node, boolean cftype) { String suffix=cftype?"":"_NODE"; switch(node.getNodeType()) { case Node.ATTRIBUTE_NODE: return "ATTRIBUTE"+suffix; case Node.CDATA_SECTION_NODE: return "CDATA_SECTION"+suffix; case Node.COMMENT_NODE: return "COMMENT"+suffix; case Node.DOCUMENT_FRAGMENT_NODE: return "DOCUMENT_FRAGMENT"+suffix; case Node.DOCUMENT_NODE: return "DOCUMENT"+suffix; case Node.DOCUMENT_TYPE_NODE: return "DOCUMENT_TYPE"+suffix; case Node.ELEMENT_NODE: return "ELEMENT"+suffix; case Node.ENTITY_NODE: return "ENTITY"+suffix; case Node.ENTITY_REFERENCE_NODE: return "ENTITY_REFERENCE"+suffix; case Node.NOTATION_NODE: return "NOTATION"+suffix; case Node.PROCESSING_INSTRUCTION_NODE: return "PROCESSING_INSTRUCTION"+suffix; case Node.TEXT_NODE: return "TEXT"+suffix; default: return "UNKNOW"+suffix; } } @Override public synchronized Element getChildWithName(String name, Element el) { Element[] children = getChildElementsAsArray(el); for(int i=0;i<children.length;i++) { if(name.equalsIgnoreCase(children[i].getNodeName())) return children[i]; } return null; } @Override public InputSource toInputSource(Resource res, Charset cs) throws IOException { String str = CFMLEngineFactory.getInstance().getIOUtil().toString((res), cs); return new InputSource(new StringReader(str)); } public InputSource toInputSource(PageContext pc, Object value) throws IOException, PageException { if(value instanceof InputSource) { return (InputSource) value; } if(value instanceof String) { return toInputSource(pc, (String)value); } if(value instanceof StringBuffer) { return toInputSource(pc, value.toString()); } CFMLEngine engine = CFMLEngineFactory.getInstance(); IO io = engine.getIOUtil(); if(value instanceof Resource) { String str = io.toString(((Resource)value), (Charset)null); return new InputSource(new StringReader(str)); } if(value instanceof File) { String str = io.toString(engine.getCastUtil().toResource(((File)value)),(Charset)null); return new InputSource(new StringReader(str)); } if(value instanceof InputStream) { InputStream is = (InputStream)value; try { String str = io.toString(is, (Charset)null); return new InputSource(new StringReader(str)); } finally { io.closeSilent(is); } } if(value instanceof Reader) { Reader reader = (Reader)value; try { String str = io.toString(reader); return new InputSource(new StringReader(str)); } finally { io.closeSilent(reader); } } if(value instanceof byte[]) { return new InputSource(new ByteArrayInputStream((byte[])value)); } throw engine.getExceptionUtil().createXMLException("cat cast object of type ["+value+"] to a Input for xml parser"); } public InputSource toInputSource(PageContext pc, String xml) throws IOException, PageException { return toInputSource(pc, xml,true); } public InputSource toInputSource(PageContext pc, String xml, boolean canBePath) throws IOException, PageException { // xml text xml=xml.trim(); if(!canBePath || xml.startsWith("<")) { return new InputSource(new StringReader(xml)); } // xml link if(pc==null)pc=CFMLEngineFactory.getInstance().getThreadPageContext(); Resource res = CFMLEngineFactory.getInstance().getResourceUtil().toResourceExisting(pc, xml); return toInputSource(pc, res); } @Override public Struct validate(InputSource xml, InputSource schema, String strSchema) throws PageException { return new XMLValidator(schema,strSchema).validate(xml); } @Override public void prependChild(Element parent, Element child) { Node first = parent.getFirstChild(); if(first==null)parent.appendChild(child); else { parent.insertBefore(child, first); } } @Override public void setFirst(Node parent, Node node) { Node first = parent.getFirstChild(); if(first!=null) parent.insertBefore(node, first); else parent.appendChild(node); } public XMLReader createXMLReader(String oprionalDefaultSaxParser) throws SAXException { try{ return XMLReaderFactory.createXMLReader(oprionalDefaultSaxParser); } catch(Throwable t){ if(t instanceof ThreadDeath) throw (ThreadDeath)t; return XMLReaderFactory.createXMLReader(); } } @Override public InputSource toInputSource(Object value) throws IOException, PageException { if(value instanceof InputSource) { return (InputSource) value; } if(value instanceof String) { return toInputSource(CFMLEngineFactory.getInstance().getThreadPageContext(), (String)value,true); } if(value instanceof StringBuffer) { return toInputSource(CFMLEngineFactory.getInstance().getThreadPageContext(), value.toString(),true); } if(value instanceof Resource) { IO io = CFMLEngineFactory.getInstance().getIOUtil(); String str = io.toString(((Resource)value), (Charset)null); return new InputSource(new StringReader(str)); } if(value instanceof File) { CFMLEngine e = CFMLEngineFactory.getInstance(); String str = e.getIOUtil().toString(e.getCastUtil().toResource(value),(Charset)null); return new InputSource(new StringReader(str)); } if(value instanceof InputStream) { InputStream is = (InputStream)value; IO io = CFMLEngineFactory.getInstance().getIOUtil(); try { String str = io.toString(is, (Charset)null); return new InputSource(new StringReader(str)); } finally { io.closeSilent(is); } } if(value instanceof Reader) { Reader reader = (Reader)value; IO io = CFMLEngineFactory.getInstance().getIOUtil(); try { String str = io.toString(reader); return new InputSource(new StringReader(str)); } finally { io.closeSilent(reader); } } if(value instanceof byte[]) { return new InputSource(new ByteArrayInputStream((byte[])value)); } throw CFMLEngineFactory.getInstance().getExceptionUtil().createExpressionException("cat cast object of type ["+value+"] to a Input for xml parser"); } @Override public void writeTo(Node node, Resource file) throws PageException { OutputStream os=null; CFMLEngine e = CFMLEngineFactory.getInstance(); IO io = e.getIOUtil(); try { os=io.toBufferedOutputStream(file.getOutputStream()); writeTo(node, new StreamResult(os),false,false,null,null,null); } catch(IOException ioe){ throw e.getCastUtil().toPageException(ioe); } finally { e.getIOUtil().closeSilent(os); } } public void writeTo(Node node,Result res,boolean omitXMLDecl,boolean indent, String publicId,String systemId,String encoding) throws PageException { try { Transformer t = getTransformerFactory().newTransformer(); t.setOutputProperty(OutputKeys.INDENT,indent?"yes":"no"); t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,omitXMLDecl?"yes":"no"); //t.setOutputProperty("{http://xml.apache.org/xalan}indent-amount", "2"); // optional properties if(!Util.isEmpty(publicId,true))t.setOutputProperty(OutputKeys.DOCTYPE_PUBLIC,publicId); if(!Util.isEmpty(systemId,true))t.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM,systemId); if(!Util.isEmpty(encoding,true))t.setOutputProperty(OutputKeys.ENCODING,encoding); t.transform(new DOMSource(node), res); } catch (Exception e) { throw CFMLEngineFactory.getInstance().getCastUtil().toPageException(e); } } @Override public String toString(Node node, boolean omitXMLDecl, boolean indent,String publicId, String systemId, String encoding) throws PageException { StringWriter sw=new StringWriter(); try { writeTo(node, new StreamResult(sw),omitXMLDecl,indent,publicId,systemId,encoding); } finally { CFMLEngineFactory.getInstance().getIOUtil().closeSilent(sw); } return sw.getBuffer().toString(); } @Override public String toString(NodeList nodes,boolean omitXMLDecl, boolean indent) throws PageException { StringWriter sw=new StringWriter(); try { int len = nodes.getLength(); for(int i=0;i<len;i++){ writeTo(nodes.item(i), new StreamResult(sw),omitXMLDecl,indent,null,null,null); } } finally { Util.closeEL(sw); } return sw.getBuffer().toString(); } @Override public String toString(Node node,String defaultValue) { StringWriter sw=new StringWriter(); try { writeTo(node, new StreamResult(sw),false,false,null,null,null); } catch(Throwable t){ if(t instanceof ThreadDeath) throw (ThreadDeath)t; return defaultValue; } finally { Util.closeEL(sw); } return sw.getBuffer().toString(); } @Override public Node toNode(Object value) throws PageException { if(value instanceof Node) return (Node)value; try { return parse(toInputSource(CFMLEngineFactory.getInstance().getThreadPageContext(), value),null,false); } catch(Exception outer) { throw CFMLEngineFactory.getInstance().getCastUtil().toPageException(outer); } } @Override public Document createDocument(Resource res, boolean isHTML) throws PageException { InputStream is=null; try { return parse(new InputSource(is=res.getInputStream()),null,isHTML); } catch (Exception e) { throw CFMLEngineFactory.getInstance().getCastUtil().toPageException(e); } finally { Util.closeEL(is); } } @Override public Document createDocument(String xml, boolean isHTML) throws PageException { try { return parse(toInputSource(null, xml),null,isHTML); } catch (Exception e) { throw CFMLEngineFactory.getInstance().getCastUtil().toPageException(e); } } @Override public Document createDocument(InputStream is, boolean isHTML) throws PageException { try { return parse(new InputSource(is),null,isHTML); } catch (Exception e) { throw CFMLEngineFactory.getInstance().getCastUtil().toPageException(e); } } static class SimpleErrorListener implements ErrorListener { public static final ErrorListener THROW_FATAL = new SimpleErrorListener(false,true,true); public static final ErrorListener THROW_ERROR = new SimpleErrorListener(false,false,true); public static final ErrorListener THROW_WARNING = new SimpleErrorListener(false,false,false); private boolean ignoreFatal; private boolean ignoreError; private boolean ignoreWarning; public SimpleErrorListener(boolean ignoreFatal, boolean ignoreError, boolean ignoreWarning){ this.ignoreFatal=ignoreFatal; this.ignoreError=ignoreError; this.ignoreWarning=ignoreWarning; } @Override public void error(TransformerException te) throws TransformerException { if(!ignoreError) throw te; } @Override public void fatalError(TransformerException te) throws TransformerException { if(!ignoreFatal) throw te; } @Override public void warning(TransformerException te) throws TransformerException { if(!ignoreWarning) throw te; } } static class XMLEntityResolverDefaultHandler extends DefaultHandler { private InputSource entityRes; public XMLEntityResolverDefaultHandler(InputSource entityRes) { this.entityRes=entityRes; } @Override public InputSource resolveEntity(String publicID, String systemID) throws SAXException { //if(entityRes!=null)print.out("resolveEntity("+(entityRes!=null)+"):"+publicID+":"+systemID); if(entityRes!=null) return entityRes; try { CFMLEngine engine = CFMLEngineFactory.getInstance(); return new InputSource( engine.getIOUtil().toBufferedInputStream(engine.getHTTPUtil().toURL(systemID).openStream())); } catch(Throwable t) { if(t instanceof ThreadDeath) throw (ThreadDeath)t; return null; } } } static class ThrowingErrorHandler implements ErrorHandler { private boolean throwFatalError; private boolean throwError; private boolean throwWarning; public ThrowingErrorHandler(boolean throwFatalError,boolean throwError,boolean throwWarning) { this.throwFatalError=throwFatalError; this.throwError=throwError; this.throwWarning=throwWarning; } @Override public void error(SAXParseException e) throws SAXException { if(throwError)throw new SAXException(e); } @Override public void fatalError(SAXParseException e) throws SAXException { if(throwFatalError)throw new SAXException(e); } @Override public void warning(SAXParseException e) throws SAXException { if(throwWarning)throw new SAXException(e); } } }