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

ch.qos.logback.core.joran.spi.Interpreter Maven / Gradle / Ivy

There is a newer version: 2.12.15
Show newest version
/**
 * 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 ch.qos.logback.core.joran.spi;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.Vector;

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

import ch.qos.logback.core.Context;
import ch.qos.logback.core.joran.action.Action;
import ch.qos.logback.core.joran.action.ImplicitAction;
import ch.qos.logback.core.joran.event.BodyEvent;
import ch.qos.logback.core.joran.event.EndEvent;
import ch.qos.logback.core.joran.event.StartEvent;
import ch.qos.logback.core.spi.ContextAwareImpl;

/**
 * Interpreter is Joran's main driving class. 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 "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 Interpreter { private static List EMPTY_LIST = new Vector(0); final private RuleStore ruleStore; final private InterpretationContext interpretationContext; final private ArrayList implicitActions; final private CAI_WithLocatorSupport cai; private ElementPath elementPath; Locator locator; EventPlayer eventPlayer; /** * The actionListStack contains a list of actions that are executing * for the given XML element. * * A list of actions is pushed by the {link #startElement} and popped by * {@link #endElement}. * */ Stack> actionListStack; /** * 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 Interpreter(Context context, RuleStore rs, ElementPath initialElementPath) { this.cai = new CAI_WithLocatorSupport(context, this); ruleStore = rs; interpretationContext = new InterpretationContext(context, this); implicitActions = new ArrayList(3); this.elementPath = initialElementPath; actionListStack = new Stack>(); eventPlayer = new EventPlayer(this); } public EventPlayer getEventPlayer() { return eventPlayer; } public void setInterpretationContextPropertiesMap(Map propertiesMap) { interpretationContext.setPropertiesMap(propertiesMap); } /** * @deprecated replaced by {@link #getInterpretationContext()} */ public InterpretationContext getExecutionContext() { return getInterpretationContext(); } public InterpretationContext getInterpretationContext() { 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 pushEmptyActionList(); return; } List applicableActionList = getApplicableActionList(elementPath, atts); if (applicableActionList != null) { actionListStack.add(applicableActionList); callBeginAction(applicableActionList, tagName, atts); } else { // every startElement pushes an action list pushEmptyActionList(); String errMsg = "no applicable action for [" + tagName + "], current ElementPath is [" + elementPath + "]"; cai.addError(errMsg); } } /** * This method is used to */ private void pushEmptyActionList() { actionListStack.add(EMPTY_LIST); } public void characters(BodyEvent be) { setDocumentLocator(be.locator); String body = be.getText(); List applicableActionList = actionListStack.peek(); if (body != null) { body = body.trim(); if (body.length() > 0) { // System.out.println("calling body method with ["+body+ "]"); callBodyAction(applicableActionList, 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 list is always pushed for every startElement, we // need // to always pop for every endElement List applicableActionList = (List) actionListStack.pop(); if (skip != null) { if (skip.equals(elementPath)) { skip = null; } } else if (applicableActionList != EMPTY_LIST) { callEndAction(applicableActionList, getTagName(localName, qName)); } // given that we always push, we must also pop the pattern elementPath.pop(); } public Locator getLocator() { return locator; } 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 addImplicitAction(ImplicitAction ia) { implicitActions.add(ia); } /** * Check if any implicit actions are applicable. As soon as an applicable * action is found, it is returned. Thus, the returned list will have at most * one element. */ List lookupImplicitAction(ElementPath elementPath, Attributes attributes, InterpretationContext ec) { int len = implicitActions.size(); for (int i = 0; i < len; i++) { ImplicitAction ia = (ImplicitAction) implicitActions.get(i); if (ia.isApplicable(elementPath, attributes, ec)) { List actionList = new ArrayList(1); actionList.add(ia); return actionList; } } return null; } /** * Return the list of applicable patterns for this */ List getApplicableActionList(ElementPath elementPath, Attributes attributes) { List applicableActionList = ruleStore.matchActions(elementPath); // logger.debug("set of applicable patterns: " + applicableActionList); if (applicableActionList == null) { applicableActionList = lookupImplicitAction(elementPath, attributes, interpretationContext); } return applicableActionList; } void callBeginAction(List applicableActionList, String tagName, Attributes atts) { if (applicableActionList == null) { return; } Iterator i = applicableActionList.iterator(); while (i.hasNext()) { Action action = (Action) i.next(); // now let us invoke the action. We catch and report any eventual // exceptions try { action.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(List applicableActionList, String body) { if (applicableActionList == null) { return; } Iterator i = applicableActionList.iterator(); while (i.hasNext()) { Action action = i.next(); try { action.body(interpretationContext, body); } catch (ActionException ae) { cai.addError("Exception in end() methd for action [" + action + "]", ae); } } } private void callEndAction(List applicableActionList, String tagName) { if (applicableActionList == null) { return; } // logger.debug("About to call end actions on node: [" + localName + "]"); Iterator i = applicableActionList.iterator(); while (i.hasNext()) { Action action = i.next(); // now let us invoke the end method of the action. We catch and report // any eventual exceptions try { action.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 Interpreter} 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, Interpreter interpreter) { super(context, interpreter); } @Override protected Object getOrigin() { Interpreter i = (Interpreter) super.getOrigin(); Locator locator = i.locator; if (locator != null) { return Interpreter.class.getName() + "@" + locator.getLineNumber() + ":" + locator.getColumnNumber(); } else { return Interpreter.class.getName() + "@NA:NA"; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy