blob: 89d20ef7a7084825e60989c759def90eeeeed8c6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 IBM Corporation and Others
* All rights reserved. This program and the accompanying materials
* are 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:
* Hisashi MIYASHITA - initial API and implementation
*******************************************************************************/
package org.eclipse.actf.ai.xmlstore.nvdl.dispatcher;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.validation.Schema;
import javax.xml.validation.TypeInfoProvider;
import javax.xml.validation.ValidatorHandler;
import org.eclipse.actf.ai.xmlstore.nvdl.NVDLException;
import org.eclipse.actf.ai.xmlstore.nvdl.fm.Interpretation;
import org.eclipse.actf.ai.xmlstore.nvdl.fm.PDA;
import org.eclipse.actf.ai.xmlstore.nvdl.model.NVDLAction;
import org.eclipse.actf.ai.xmlstore.nvdl.model.NVDLAllowAction;
import org.eclipse.actf.ai.xmlstore.nvdl.model.NVDLConst;
import org.eclipse.actf.ai.xmlstore.nvdl.model.NVDLElement;
import org.eclipse.actf.ai.xmlstore.nvdl.model.NVDLRejectAction;
import org.eclipse.actf.ai.xmlstore.nvdl.model.NVDLRules;
import org.eclipse.actf.ai.xmlstore.nvdl.model.NVDLValidateAction;
import org.eclipse.actf.ai.xmlstore.nvdl.util.Log;
import org.eclipse.actf.ai.xmlstore.nvdl.util.PrefixMapper;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
/**
* The <code>NVDLSAXDispatcher</code> is a dispatcher for SAX interface.
*/
public class NVDLSAXDispatcher {
private NVDLRules rules;
private PDA pda;
private final boolean emitSectionID;
private PrefixMapper prefixMapper = new PrefixMapper();
// NVDLElement -> List of ActiveHandlers
private Map<NVDLElement, List<ActiveHandler>> activeHandlersMap = new HashMap<NVDLElement, List<ActiveHandler>>();
private Set<ActiveHandler> activeHandlers = new HashSet<ActiveHandler>();
private int errorCounter = 0;
public void reset() {
pda.reset();
prefixMapper.reset();
activeHandlersMap.clear();
activeHandlers.clear();
errorCounter = 0;
currentElement = null;
nextSectionIdx = 0;
idMap.clear();
asnIDCounter = 0;
}
private XMLReader reader;
public int getErrorCount() {
return errorCounter;
}
private void incrementErrorCounter() {
errorCounter++;
}
private void sendVirtualElement(ContentHandler ch,
String instancePrefix,
String qName,
Attributes attrs,
boolean requireDecl)
throws SAXException {
Object effectiveMapping = prefixMapper.startEffectivePrefixMappings(ch);
if (requireDecl) {
ch.startPrefixMapping(instancePrefix, NVDLConst.INSTANCE_NS);
}
ch.startElement(NVDLConst.INSTANCE_NS,
NVDLConst.VIRTUALELEMENT_NAME,
qName, attrs);
ch.endElement(NVDLConst.INSTANCE_NS,
NVDLConst.VIRTUALELEMENT_NAME,
qName);
if (requireDecl) {
ch.endPrefixMapping(instancePrefix);
}
prefixMapper.endEffectivePrefixMappings(effectiveMapping, ch);
}
private NVDLAttributes addASNIDAttribute(Interpretation ip,
ContentHandler h,
NVDLAttributes attr,
String asnID)
throws SAXException {
if (h == null) return attr;
PrefixMapper.PrefixReturnVal prv = prefixMapper.uniquePrefix(NVDLConst.INSTANCE_REC_PREFIX_BASE,
NVDLConst.INSTANCE_REC_NS);
String qName = prv.prefix + ":" + NVDLConst.ASN_ID_ATTR;
if (prv.requireDecl) {
h.startPrefixMapping(prv.prefix, NVDLConst.INSTANCE_REC_NS);
prefixMapper.startPrefixMapping(prv.prefix, NVDLConst.INSTANCE_REC_NS);
ip.setPrefix(prv.prefix);
}
attr.addExtAttribute(NVDLConst.INSTANCE_REC_NS,
NVDLConst.ASN_ID_ATTR,
qName, asnID);
return attr;
}
private void dispatchAttributeSection(List<Interpretation> ips,
NVDLAttributes attrs,
String asnID)
throws SAXException {
Iterator<Interpretation> it = ips.iterator();
while (it.hasNext()) {
Interpretation ip = it.next();
if (!ip.isDispatch()) continue;
InterpretationSlot slot = setupInterpretationSlot(ip, currentElement, true);
ContentHandler h = slot.contentHandler;
ContentHandler dh = slot.contentHandlerForDebug;
PrefixMapper.PrefixReturnVal prv;
prv = prefixMapper.uniquePrefix(NVDLConst.INSTANCE_PREFIX_BASE,
NVDLConst.INSTANCE_NS);
String qName = prv.prefix + ":" + NVDLConst.VIRTUALELEMENT_NAME;
sendVirtualElement(h, prv.prefix, qName, attrs, prv.requireDecl);
if (dh != null) {
if (emitSectionID) {
attrs = addASNIDAttribute(ip, dh, attrs, asnID);
}
sendVirtualElement(dh, prv.prefix, qName, attrs, prv.requireDecl);
}
}
}
private NVDLAttributes dispatchAttribute(Interpretation ip,
ContentHandler h,
Attributes attrs)
throws SAXException {
Map<String, NVDLAttributes> attrsMap = new HashMap<String, NVDLAttributes>();
int len = attrs.getLength();
NVDLAttributes restAttributes = new NVDLAttributes(currentElement, attrs);
for (int i = 0; i < len; i++) {
String ns = attrs.getURI(i);
NVDLAttributes pa = attrsMap.get(ns);
if (pa == null) {
pa = new NVDLAttributes(currentElement, attrs, ns);
attrsMap.put(ns, pa);
}
pa.addAttribute(i);
}
Iterator<NVDLAttributes> it = attrsMap.values().iterator();
boolean isASNIDRequired = false;
String asnID = generateASNID();
while (it.hasNext()) {
NVDLAttributes pa = it.next();
List<Interpretation> ipAttrs = pda.getAttributeInterpretation(ip, pa);
if (pda.isAttributeAttached()) {
restAttributes.addAttributes(pa);
} else {
isASNIDRequired = emitSectionID;
}
dispatchAttributeSection(ipAttrs, pa, asnID);
}
if (isASNIDRequired) {
restAttributes = addASNIDAttribute(ip, h, restAttributes, asnID);
}
return restAttributes;
}
private class InterpretationSlot implements ErrorHandler {
ContentHandler contentHandler;
ContentHandler contentHandlerForDebug;
String definedPrefix;
final Interpretation ip;
/********************************************************************************
Error Handler Proxy
********************************************************************************/
private final ErrorHandler errorHandler;
private SAXParseException encapsulateException(SAXParseException e) {
NVDLDispatcherException de = new NVDLDispatcherException(e.getException(),
ip.getAction());
return new SAXParseException(e.getMessage(),
e.getPublicId(),
e.getSystemId(),
e.getLineNumber(),
e.getColumnNumber(), de);
}
public void warning(SAXParseException e)
throws SAXException {
if (errorHandler != null)
errorHandler.warning(encapsulateException(e));
}
public void error(SAXParseException e)
throws SAXException {
incrementErrorCounter();
if (errorHandler != null)
errorHandler.error(encapsulateException(e));
}
public void fatalError(SAXParseException e)
throws SAXException {
incrementErrorCounter();
if (errorHandler != null)
errorHandler.fatalError(encapsulateException(e));
}
InterpretationSlot(Interpretation ip,
ContentHandler contentHandler,
ContentHandler contentHandlerForDebug) {
this.ip = ip;
this.contentHandler = contentHandler;
this.contentHandlerForDebug = contentHandlerForDebug;
errorHandler = validatorHandler.getErrorHandler();
if (contentHandler instanceof ValidatorHandler) {
((ValidatorHandler) contentHandler).setErrorHandler(this);
}
}
}
private static class IDMapVal {
int sectionID;
int slotNodeID;
}
private int asnIDCounter;
private String generateASNID() {
return Integer.toString(asnIDCounter++);
}
private HashMap<String, IDMapVal> idMap = new HashMap<String, IDMapVal>();
private String generateID(Interpretation ip, boolean sectionID, boolean increment) {
String id = ip.getID();
String idForNum;
if (sectionID) {
idForNum = ip.getPrevID();
} else {
idForNum = id;
}
// System.err.println("!"+id+" / "+idForNum);
IDMapVal idVal = idMap.get(idForNum);
int idNum;
if (idVal == null) {
idNum = 0;
idMap.put(idForNum, new IDMapVal());
} else {
idNum = idVal.slotNodeID;
if (!sectionID && increment) {
idVal.slotNodeID++;
}
}
if (idNum == 0) {
return id;
} else {
return id + " " + idNum;
}
}
private InterpretationSlot setupInterpretationSlot(Interpretation ip,
NVDLElement e,
boolean forAttribute)
throws SAXException {
InterpretationSlot slot = (InterpretationSlot) ip.getSlot();
if (slot != null) return slot;
NVDLAction a = ip.getAction();
ContentHandler contentHandler = makeContentHandler(a, forAttribute);
ContentHandler contentHandlerForDebug = setupContentHandlerForDebug(generateID(ip, true, false), a);
if (contentHandler != null) {
startActiveHandler(contentHandler, e);
}
if (contentHandlerForDebug != null) {
startActiveHandler(contentHandlerForDebug, e);
}
slot = new InterpretationSlot(ip, contentHandler, contentHandlerForDebug);
ip.setSlot(slot);
return slot;
}
private InterpretationSlot getInterpretationSlot(Interpretation ip) {
return (InterpretationSlot) ip.getSlot();
}
// --------------------------------------------------------------------------------
// TODO: Debug SAX Handler
// --------------------------------------------------------------------------------
public interface DebugHandlerFactory {
ContentHandler createContentHandler(String id, NVDLAction action) throws SAXException;
void nextActionHandler(NVDLAction action, Locator locator);
}
DebugHandlerFactory debugHandlerFactory;
public void setDebugHandlerFactory(DebugHandlerFactory f) {
debugHandlerFactory = f;
}
private ContentHandler setupContentHandlerForDebug(String id, NVDLAction a) throws SAXException {
if (debugHandlerFactory == null) return null;
debugHandlerFactory.nextActionHandler(a, validatorHandler.locator);
ContentHandler h = debugHandlerFactory.createContentHandler(id, a);
if (h == null) return null;
if (validatorHandler.locator != null) {
h.setDocumentLocator(validatorHandler.locator);
}
return h;
}
// --------------------------------------------------------------------------------
private int nextSectionIdx;
private NVDLElement currentElement;
private NVDLElement createNVDLElement(String ns, String localName, NVDLElement parent) {
NVDLElement e = new NVDLElement(ns, localName, parent,
rules.getTriggerManager(),
nextSectionIdx);
if (e.isSectionHead()) nextSectionIdx++;
return e;
}
static class ActiveHandler {
ContentHandler handler;
NVDLElement rootElement;
Object effectiveMapping;
}
private ActiveHandler startActiveHandler(ContentHandler h,
NVDLElement e)
throws SAXException {
ActiveHandler ah = new ActiveHandler();
ah.handler = h;
ah.rootElement = e;
h.startDocument();
ah.effectiveMapping = prefixMapper.startEffectivePrefixMappings(h);
activeHandlers.add(ah);
List<ActiveHandler> ahs = activeHandlersMap.get(e);
if (ahs == null) {
ahs = new ArrayList<ActiveHandler>();
activeHandlersMap.put(e, ahs);
}
ahs.add(ah);
return ah;
}
private void endActiveHandlers(NVDLElement e) throws SAXException {
List<ActiveHandler> ahs = activeHandlersMap.get(e);
if (ahs == null) return;
Iterator<ActiveHandler> it = ahs.iterator();
while (it.hasNext()) {
ActiveHandler ah = it.next();
prefixMapper.endEffectivePrefixMappings(ah.effectiveMapping, ah.handler);
ah.handler.endDocument();
activeHandlers.remove(ah);
}
activeHandlersMap.put(e, null);
}
private ContentHandler makeContentHandlerForValidateAction(NVDLValidateAction validateAction,
boolean forAttribute)
throws SAXException {
Schema schema;
ValidatorHandler h;
try {
schema = validateAction.getSchema(forAttribute);
} catch (NVDLException e) {
if (e.getException() instanceof SAXException) {
throw (SAXException) e.getException();
}
throw new SAXException(e);
}
h = schema.newValidatorHandler();
// I think it's a bug of isorelax JARV bridge.
h.setContentHandler(null);
return h;
}
private ContentHandler makeContentHandler(NVDLAction a,
boolean forAttribute) throws SAXException {
ContentHandler contentHandler;
if (a instanceof NVDLValidateAction) {
NVDLValidateAction va = (NVDLValidateAction) a;
contentHandler = makeContentHandlerForValidateAction(va, forAttribute);
} else if (a instanceof NVDLAllowAction) {
contentHandler = new AllowValidatorHandler();
} else if (a instanceof NVDLRejectAction) {
contentHandler = new RejectValidatorHandler();
} else {
return null;
}
// TODO: redesign.
if (validatorHandler.locator != null) {
contentHandler.setDocumentLocator(validatorHandler.locator);
}
return contentHandler;
}
private class NVDLValidatorHandler extends ValidatorHandler {
private ContentHandler contentHandler = null;
public void setContentHandler(ContentHandler contentHandler) {
this.contentHandler = contentHandler;
}
public ContentHandler getContentHandler() {
return contentHandler;
}
private ErrorHandler errorHandler = null;
public void setErrorHandler(ErrorHandler errorHandler) {
this.errorHandler = errorHandler;
}
public ErrorHandler getErrorHandler() {
return errorHandler;
}
private LSResourceResolver resourceResolver = null;
public void setResourceResolver(LSResourceResolver resourceResolver) {
this.resourceResolver = resourceResolver;
}
public LSResourceResolver getResourceResolver() {
return resourceResolver;
}
public TypeInfoProvider getTypeInfoProvider() {
// TODO: we should appropriately forward type info requests.
return null;
}
Locator locator;
public void setDocumentLocator(Locator locator) {
this.locator = locator;
Iterator<ActiveHandler> it = activeHandlers.iterator();
while (it.hasNext()) {
ActiveHandler ah = it.next();
ah.handler.setDocumentLocator(locator);
}
}
public void startDocument() throws SAXException {
// Do nothing.
}
public void endDocument() throws SAXException {
Iterator<ActiveHandler> it = activeHandlers.iterator();
while (it.hasNext()) {
ActiveHandler ah = it.next();
Log.error("All handlers must be deactivated." + ah);
ah.handler.endDocument();
}
activeHandlers.clear();
}
public void startPrefixMapping(String prefix, String uri)
throws SAXException {
prefixMapper.startPrefixMapping(prefix, uri);
Iterator<ActiveHandler> it = activeHandlers.iterator();
while (it.hasNext()) {
ActiveHandler ah = it.next();
ah.handler.startPrefixMapping(prefix, uri);
}
}
public void endPrefixMapping(String prefix) throws SAXException {
prefixMapper.endPrefixMapping(prefix);
Iterator<ActiveHandler> it = activeHandlers.iterator();
while (it.hasNext()) {
ActiveHandler ah = it.next();
ah.handler.endPrefixMapping(prefix);
}
}
private void dispatchPlaceHolder(ContentHandler h,
String uri,
String localName) throws SAXException {
Attributes ats = new NVDLPlaceHolderAttributes(uri, localName);
prefixMapper.sendEmptyElement(h,
NVDLConst.INSTANCE_NS,
NVDLConst.PLACEHOLDER_NAME,
NVDLConst.INSTANCE_PREFIX_BASE,
ats);
}
private void dispatchElementSlotNodeStart(Interpretation ip,
ContentHandler h) throws SAXException {
Attributes ats = new NVDLSlotNodeAttributes(NVDLConst.SLOT_NODE_ID_ATTR,
generateID(ip, false, false));
prefixMapper.sendEmptyElement(h,
NVDLConst.INSTANCE_REC_NS,
NVDLConst.SLOT_NODE_START_NAME,
NVDLConst.INSTANCE_REC_PREFIX_BASE,
ats);
}
private void dispatchElementSlotNodeEnd(Interpretation ip,
ContentHandler h) throws SAXException {
Attributes ats = new NVDLSlotNodeAttributes(NVDLConst.SLOT_NODE_ID_ATTR,
generateID(ip, false, true));
prefixMapper.sendEmptyElement(h,
NVDLConst.INSTANCE_REC_NS,
NVDLConst.SLOT_NODE_END_NAME,
NVDLConst.INSTANCE_REC_PREFIX_BASE,
ats);
}
private void addSectionIDAttribute(Interpretation ip,
ContentHandler h,
NVDLAttributes attr) throws SAXException {
if (h == null) return;
PrefixMapper.PrefixReturnVal prv = prefixMapper.uniquePrefix(NVDLConst.INSTANCE_REC_PREFIX_BASE,
NVDLConst.INSTANCE_REC_NS);
String qName = prv.prefix + ":" + NVDLConst.SECTION_ID_ATTR;
if (prv.requireDecl) {
h.startPrefixMapping(prv.prefix, NVDLConst.INSTANCE_REC_NS);
prefixMapper.startPrefixMapping(prv.prefix, NVDLConst.INSTANCE_REC_NS);
ip.setPrefix(prv.prefix);
}
attr.addExtAttribute(NVDLConst.INSTANCE_REC_NS,
NVDLConst.SECTION_ID_ATTR,
qName,
generateID(ip, true, true));
}
public void startElement(String uri,
String localName,
String qName,
Attributes attrs) throws SAXException {
currentElement = createNVDLElement(uri, localName, currentElement);
if (currentElement.isSectionHead()) {
List prevIps = pda.getCurrentInterpretations();
Iterator prevIt = prevIps.iterator();
while (prevIt.hasNext()) {
Interpretation ip = (Interpretation) prevIt.next();
// Interpretation ipHead = ip.getSectionHeadInterpretation();
Interpretation ipHead = ip.getEffectiveInterpretation();
if (ipHead == null) continue;
if (!ipHead.isDispatch()) continue;
InterpretationSlot slot = setupInterpretationSlot(ipHead, currentElement, false);
ContentHandler dh = slot.contentHandlerForDebug;
if (dh == null) continue;
dispatchElementSlotNodeStart(ip, dh);
}
}
pda.startElement(currentElement);
List ips = pda.getCurrentInterpretations();
Iterator it = ips.iterator();
while (it.hasNext()) {
Interpretation ip = (Interpretation) it.next();
Interpretation ipDispatch = ip.getEffectiveInterpretation();
if (ipDispatch == null) continue;
InterpretationSlot slot = setupInterpretationSlot(ipDispatch, currentElement, false);
ContentHandler h = slot.contentHandler;
ContentHandler dh = slot.contentHandlerForDebug;
if (ip.isAttachPlaceHolder()) {
if (currentElement.isSectionHead()) {
dispatchPlaceHolder(h, uri, localName);
if (dh != null) {
dispatchPlaceHolder(dh, uri, localName);
}
}
} else {
NVDLAttributes rest = dispatchAttribute(ip, dh, attrs);
h.startElement(uri, localName, qName, rest);
if (dh != null) {
if (emitSectionID && currentElement.isSectionHead()) {
addSectionIDAttribute(ip, dh, rest);
}
dh.startElement(uri, localName, qName, rest);
}
}
}
}
public void endElement(String uri, String localName, String qName)
throws SAXException {
List ips = pda.getCurrentInterpretations();
Iterator it = ips.iterator();
if (currentElement == null) {
Log.error("Something wrong happens. Some tags are not balanced.");
return;
}
while (it.hasNext()) {
Interpretation ip = (Interpretation) it.next();
if (ip.isAttachPlaceHolder()) continue;
Interpretation ipDispatch = ip.getEffectiveInterpretation();
if (ipDispatch == null) continue;
InterpretationSlot slot = setupInterpretationSlot(ipDispatch, currentElement, false);
ContentHandler h = slot.contentHandler;
ContentHandler dh = slot.contentHandlerForDebug;
h.endElement(uri, localName, qName);
if (dh != null) {
dh.endElement(uri, localName, qName);
if (ip.getPrefix() != null) {
prefixMapper.endPrefixMapping(ip.getPrefix());
dh.endPrefixMapping(ip.getPrefix());
ip.setPrefix(null);
}
}
}
endActiveHandlers(currentElement);
pda.endElement();
if (currentElement.isSectionHead()) {
List prevIps = pda.getCurrentInterpretations();
Iterator prevIt = prevIps.iterator();
while (prevIt.hasNext()) {
Interpretation ip = (Interpretation) prevIt.next();
Interpretation ipHead = ip.getEffectiveInterpretation();
if (ipHead == null) continue;
if (!ipHead.isDispatch()) continue;
InterpretationSlot slot = setupInterpretationSlot(ipHead, currentElement, false);
ContentHandler dh = slot.contentHandlerForDebug;
if (dh == null) continue;
dispatchElementSlotNodeEnd(ip, dh);
}
}
currentElement = currentElement.parent;
}
public void characters(char[] ch, int start, int length)
throws SAXException {
List ips = pda.getCurrentInterpretations();
Iterator it = ips.iterator();
while (it.hasNext()) {
Interpretation ip = (Interpretation) it.next();
if (ip.isAttachPlaceHolder()) continue;
Interpretation ipDispatch = ip.getEffectiveInterpretation();
if (ipDispatch == null) continue;
InterpretationSlot slot = getInterpretationSlot(ipDispatch);
ContentHandler h = slot.contentHandler;
ContentHandler dh = slot.contentHandlerForDebug;
h.characters(ch, start, length);
if (dh != null) {
dh.characters(ch, start, length);
}
}
}
public void ignorableWhitespace(char[] ch,
int start,
int length) throws SAXException {
List ips = pda.getCurrentInterpretations();
Iterator it = ips.iterator();
while (it.hasNext()) {
Interpretation ip = (Interpretation) it.next();
if (ip.isAttachPlaceHolder()) continue;
Interpretation ipDispatch = ip.getEffectiveInterpretation();
if (ipDispatch == null) continue;
InterpretationSlot slot = getInterpretationSlot(ipDispatch);
ContentHandler h = slot.contentHandler;
ContentHandler dh = slot.contentHandlerForDebug;
h.ignorableWhitespace(ch, start, length);
if (dh != null) {
dh.ignorableWhitespace(ch, start, length);
}
}
}
public void processingInstruction(String target,
String data) throws SAXException {
}
public void skippedEntity(String name) throws SAXException {
// Currently do not dispatch.
}
}
private final NVDLValidatorHandler validatorHandler = new NVDLValidatorHandler();
public ValidatorHandler getValidatorHandler() {
return validatorHandler;
}
public boolean validate(InputSource is, ErrorHandler eh)
throws SAXException, IOException {
ValidatorHandler h = getValidatorHandler();
h.setErrorHandler(eh);
reader.setErrorHandler(eh);
reader.parse(is);
if (getErrorCount() > 0) return false;
return true;
}
public NVDLSAXDispatcher(NVDLRules rules, boolean emitSectionID) throws SAXException {
this.rules = rules;
this.pda = new PDA(rules);
this.emitSectionID = emitSectionID;
reader = XMLReaderFactory.createXMLReader();
reader.setFeature("http://xml.org/sax/features/namespaces", true);
reader.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
ValidatorHandler h = getValidatorHandler();
reader.setContentHandler(h);
}
}