All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juli.logging.ch.qos.logback.core.joran.spi.SaxEventInterpreter Maven / Gradle / Ivy

/**
 * Logback: the reliable, generic, fast and flexible logging framework.
 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
 *
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *
 *   or (per the licensee's choosing)
 *
 * under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation.
 */
package org.apache.juli.logging.ch.qos.logback.core.joran.spi;

import java.util.List;
import java.util.Stack;
import java.util.function.Supplier;

import org.xml.sax.Attributes;
import org.xml.sax.Locator;

import org.apache.juli.logging.ch.qos.logback.core.Context;
import org.apache.juli.logging.ch.qos.logback.core.joran.action.Action;
import org.apache.juli.logging.ch.qos.logback.core.joran.action.NOPAction;
import org.apache.juli.logging.ch.qos.logback.core.joran.event.BodyEvent;
import org.apache.juli.logging.ch.qos.logback.core.joran.event.EndEvent;
import org.apache.juli.logging.ch.qos.logback.core.joran.event.SaxEvent;
import org.apache.juli.logging.ch.qos.logback.core.joran.event.StartEvent;
import org.apache.juli.logging.ch.qos.logback.core.spi.ContextAwareImpl;

/**
 * {@code SaxEventInterpreter} is Joran's driving class for handling "low-level"
 * SAX events. It extends SAX {@link org.xml.sax.helpers.DefaultHandler
 * DefaultHandler} which invokes various {@link Action actions} according to
 * predefined patterns.
 * 
 * 

* Patterns are kept in a {@link RuleStore} which is programmed to store and * then later produce the applicable actions for a given pattern. * *

* The pattern corresponding to a top level <a> element is the string "a". * *

* The pattern corresponding to an element <b> embedded within a top level * <a> element is the string {@code "a/b"}. * *

* The pattern corresponding to an <b> and any level of nesting is * "*/b. Thus, the * character placed at the beginning of a pattern * serves as a wildcard for the level of nesting. * * Conceptually, this is very similar to the API of commons-digester. Joran * offers several small advantages. First and foremost, it offers support for * implicit actions which result in a significant leap in flexibility. Second, * in our opinion better error reporting capability. Third, it is self-reliant. * It does not depend on other APIs, in particular commons-logging which is too * unreliable. Last but not least, Joran is quite tiny and is expected to remain * so. * * @author Ceki Gülcü * */ public class SaxEventInterpreter { private static Action NOP_ACTION_SINGLETON = new NOPAction(); final private RuleStore ruleStore; final private SaxEventInterpretationContext interpretationContext; private Supplier implicitActionSupplier; final private CAI_WithLocatorSupport cai; private ElementPath elementPath; Locator locator; EventPlayer eventPlayer; Context context; /** * The actionStack contain the action that is executing * for the given XML element. * * An action is pushed by the {link #startElement} and popped by * {@link #endElement}. * */ Stack actionStack; /** * If the skip nested is set, then we skip all its nested elements until it is * set back to null at when the element's end is reached. */ ElementPath skip = null; public SaxEventInterpreter(Context context, RuleStore rs, ElementPath initialElementPath, List saxEvents) { this.context = context; this.cai = new CAI_WithLocatorSupport(context, this); ruleStore = rs; interpretationContext = new SaxEventInterpretationContext(context, this); this.elementPath = initialElementPath; actionStack = new Stack<>(); eventPlayer = new EventPlayer(this, saxEvents); } public EventPlayer getEventPlayer() { return eventPlayer; } public ElementPath getCopyOfElementPath() { return elementPath.duplicate(); } public SaxEventInterpretationContext getSaxEventInterpretationContext() { return interpretationContext; } public void startDocument() { } public void startElement(StartEvent se) { setDocumentLocator(se.getLocator()); startElement(se.namespaceURI, se.localName, se.qName, se.attributes); } private void startElement(String namespaceURI, String localName, String qName, Attributes atts) { String tagName = getTagName(localName, qName); elementPath.push(tagName); if (skip != null) { // every startElement pushes an action list pushEmptyActionOntoActionStack(); return; } Action applicableAction = getApplicableAction(elementPath, atts); if (applicableAction != null) { actionStack.add(applicableAction); callBeginAction(applicableAction, tagName, atts); } else { // every startElement pushes an action list pushEmptyActionOntoActionStack(); String errMsg = "no applicable action for [" + tagName + "], current ElementPath is [" + elementPath + "]"; cai.addError(errMsg); } } /** * This method is used to */ private void pushEmptyActionOntoActionStack() { actionStack.push(NOP_ACTION_SINGLETON); } public void characters(BodyEvent be) { setDocumentLocator(be.locator); String body = be.getText(); Action applicableAction = actionStack.peek(); if (body != null) { body = body.trim(); if (body.length() > 0) { callBodyAction(applicableAction, body); } } } public void endElement(EndEvent endEvent) { setDocumentLocator(endEvent.locator); endElement(endEvent.namespaceURI, endEvent.localName, endEvent.qName); } private void endElement(String namespaceURI, String localName, String qName) { // given that an action is always pushed for every startElement, we // need to always pop for every endElement Action applicableAction = actionStack.pop(); if (skip != null) { if (skip.equals(elementPath)) { skip = null; } } else if (applicableAction != NOP_ACTION_SINGLETON) { callEndAction(applicableAction, getTagName(localName, qName)); } // given that we always push, we must also pop the pattern elementPath.pop(); } public Locator getLocator() { return locator; } // having the locator set as parsing progresses is quite ugly public void setDocumentLocator(Locator l) { locator = l; } String getTagName(String localName, String qName) { String tagName = localName; if ((tagName == null) || (tagName.length() < 1)) { tagName = qName; } return tagName; } public void setImplicitActionSupplier(Supplier actionSupplier) { this.implicitActionSupplier = actionSupplier; } /** * Return the list of applicable patterns for this */ Action getApplicableAction(ElementPath elementPath, Attributes attributes) { Supplier applicableActionSupplier = ruleStore.matchActions(elementPath); if (applicableActionSupplier != null) { Action applicableAction = applicableActionSupplier.get(); applicableAction.setContext(context); return applicableAction; } else { Action implicitAction = implicitActionSupplier.get(); implicitAction.setContext(context); return implicitAction; } } void callBeginAction(Action applicableAction, String tagName, Attributes atts) { if (applicableAction == null) { return; } // now let us invoke the action. We catch and report any eventual // exceptions try { applicableAction.begin(interpretationContext, tagName, atts); } catch (ActionException e) { skip = elementPath.duplicate(); cai.addError("ActionException in Action for tag [" + tagName + "]", e); } catch (RuntimeException e) { skip = elementPath.duplicate(); cai.addError("RuntimeException in Action for tag [" + tagName + "]", e); } } private void callBodyAction(Action applicableAction, String body) { if (applicableAction == null) { return; } try { applicableAction.body(interpretationContext, body); } catch (ActionException ae) { cai.addError("Exception in body() method for action [" + applicableAction + "]", ae); } } private void callEndAction(Action applicableAction, String tagName) { if (applicableAction == null) { return; } try { applicableAction.end(interpretationContext, tagName); } catch (ActionException ae) { // at this point endAction, there is no point in skipping children as // they have been already processed cai.addError("ActionException in Action for tag [" + tagName + "]", ae); } catch (RuntimeException e) { // no point in setting skip cai.addError("RuntimeException in Action for tag [" + tagName + "]", e); } } public RuleStore getRuleStore() { return ruleStore; } } /** * When {@link SaxEventInterpreter} class is used as the origin of an * {@link ContextAwareImpl} instance, then XML locator information is lost. This * class preserves locator information (as a string). * * @author ceki */ class CAI_WithLocatorSupport extends ContextAwareImpl { CAI_WithLocatorSupport(Context context, SaxEventInterpreter interpreter) { super(context, interpreter); } @Override protected Object getOrigin() { SaxEventInterpreter i = (SaxEventInterpreter) super.getOrigin(); Locator locator = i.locator; if (locator != null) { return SaxEventInterpreter.class.getName() + "@" + locator.getLineNumber() + ":" + locator.getColumnNumber(); } else { return SaxEventInterpreter.class.getName() + "@NA:NA"; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy