/**
* Copyright 2015 Santhosh Kumar Tekuri
*
* The JLibs authors license this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package jlibs.xml.sax.binding.impl;
import jlibs.core.lang.StringUtil;
import jlibs.xml.NamespaceMap;
import jlibs.xml.QNameFake;
import jlibs.xml.sax.SAXUtil;
import jlibs.xml.sax.binding.SAXContext;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import java.io.CharArrayWriter;
import java.io.IOException;
/**
* @author Santhosh Kumar T
*/
public class Handler extends DefaultHandler{
public final Registry docRegistry;
public Handler(Registry docRegistry){
this.docRegistry = docRegistry;
}
/*-------------------------------------------------[ populateNamespaces ]---------------------------------------------------*/
private boolean populateNamespaces = false;
public boolean isPopulateNamespaces(){
return populateNamespaces;
}
public void setPopulateNamespaces(boolean populateNamespaces){
this.populateNamespaces = populateNamespaces;
}
/*-------------------------------------------------[ Parsing ]---------------------------------------------------*/
public Object parse(InputSource is) throws ParserConfigurationException, SAXException, IOException{
SAXUtil.newSAXParser(true, false, false).parse(is, this);
return reset();
}
private Object rootObject;
private CharArrayWriter content = new CharArrayWriter();
private NamespaceMap.Handler nsHandler = new NamespaceMap.Handler();
private Object reset(){
Object obj = rootObject;
context = null;
rootObject = null;
content.reset();
nsHandler.namespaceMap = null;
return obj;
}
private Locator locator;
@Override
public void setDocumentLocator(Locator locator){
this.locator = locator;
}
@Override
public void startDocument() throws SAXException{
if(populateNamespaces)
nsHandler.startDocument();
reset();
}
@Override
public void startPrefixMapping(String prefix, String uri) throws SAXException{
if(populateNamespaces)
nsHandler.startPrefixMapping(prefix, uri);
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException{
if(populateNamespaces)
nsHandler.startElement();
if(context!=null)
context.onText();
context = newContext(uri, localName, attributes);
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException{
content.write(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException{
if(populateNamespaces)
nsHandler.endElement();
context.onText();
context = context.pop();
}
public Object getObject(){
return rootObject;
}
protected void onUnresolvedElement(SAXContext context) throws SAXException{
// do nothing
}
/*-------------------------------------------------[ Context ]---------------------------------------------------*/
private QNameFake qnameFake = new QNameFake();
private BindingContext context;
private BindingContext cache;
public BindingContext newContext(String namespaceURI, String localPart, Attributes attributes) throws SAXException{
if(cache==null)
return new BindingContext(namespaceURI, localPart, context, attributes);
else{
BindingContext reusingContext = cache;
cache = cache.parent;
reusingContext.init(namespaceURI, localPart, context, attributes);
return reusingContext;
}
}
@SuppressWarnings({"unchecked"})
private class BindingContext extends SAXContext{
private BindingRelation bindingRelation;
private BindingContext parent;
public BindingContext(String namespaceURI, String localPart, BindingContext parent, Attributes attributes) throws SAXException{
init(namespaceURI, localPart, parent, attributes);
}
private void init(String namespaceURI, String localPart, BindingContext parent, Attributes attributes) throws SAXException{
this.qnameFake = Handler.this.qnameFake;
namespaceMap = nsHandler.namespaceMap;
this.parent = parent;
bindingRelation = (parent!=null?parent.bindingRelation.binding.registry:docRegistry).get(qnameFake.set(namespaceURI, localPart));
boolean unresolvedElement = bindingRelation==null;
if(unresolvedElement)
bindingRelation = BindingRelation.DO_NOTHING;
this.element = bindingRelation.qname;
if(element.getNamespaceURI().equals("*") || element.getLocalPart().equals("*"))
this.element = new QName(namespaceURI, localPart);
if(parent!=null)
object = parent.object;
if(unresolvedElement)
onUnresolvedElement(this);
bindingRelation.binding.startElement(bindingRelation.bindingState, this, attributes);
if(parent!=null)
bindingRelation.relation.startRelation(bindingRelation.relationState, parent, this);
}
public void onText() throws SAXException{
bindingRelation.binding.text(bindingRelation.bindingState, this, content.toString());
content.reset();
}
public BindingContext pop() throws SAXException{
bindingRelation.binding.endElement(bindingRelation.bindingState, this);
if(parent!=null)
bindingRelation.relation.endRelation(bindingRelation.relationState, parent, this);
if(parent==null)
rootObject = object;
// reset and add to cache
BindingContext _parent = parent;
namespaceMap = null;
element = null;
object = null;
qnameFake = null;
if(temp!=null)
temp.clear();
bindingRelation = null;
parent = cache;
cache = this;
return _parent;
}
@Override
public String xpath(){
StringBuilder buff = new StringBuilder("/");
BindingContext context = this;
while(context!=null){
if(buff.length()>1)
buff.insert(1, '/');
String qname;
if(namespaceMap==null)
qname = context.element.toString();
else{
String prefix = namespaceMap.getPrefix(context.element.getNamespaceURI());
qname = StringUtil.isEmpty(prefix) ? context.element.getLocalPart() : (prefix+":"+context.element.getLocalPart());
}
buff.insert(1, qname);
context = context.parent;
}
return buff.toString();
}
public Locator locator(){
return locator;
}
}
}