001/* 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2026, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v2.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.core.joran.action; 015 016import org.xml.sax.Attributes; 017 018import ch.qos.logback.core.joran.spi.ActionException; 019import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; 020import ch.qos.logback.core.model.Model; 021 022public abstract class BaseModelAction extends Action { 023 024 Model parentModel; 025 Model currentModel; 026 boolean inError = false; 027 028 @Override 029 public void begin(SaxEventInterpretationContext saxEventInterpretationContext, String name, Attributes attributes) 030 throws ActionException { 031 parentModel = null; 032 inError = false; 033 034 if (!validPreconditions(saxEventInterpretationContext, name, attributes)) { 035 inError = true; 036 return; 037 } 038 039 currentModel = buildCurrentModel(saxEventInterpretationContext, name, attributes); 040 currentModel.setTag(name); 041 if (!saxEventInterpretationContext.isModelStackEmpty()) { 042 parentModel = saxEventInterpretationContext.peekModel(); 043 } 044 final int lineNumber = getLineNumber(saxEventInterpretationContext); 045 currentModel.setLineNumber(lineNumber); 046 saxEventInterpretationContext.pushModel(currentModel); 047 } 048 049 /** 050 * Builds and returns a Model instance for the current XML element being processed. 051 * 052 * <p>This method is called during the begin phase of XML processing to create a Model 053 * object that represents the current element. The returned model will be configured with 054 * the element's tag name, line number, and will be pushed onto the model stack.</p> 055 * 056 * @param interpretationContext the context for interpreting SAX events, providing access to 057 * the model stack and other interpretation state 058 * @param name the name of the XML element being processed 059 * @param attributes the attributes of the XML element 060 * @return a new Model instance representing the current XML element 061 */ 062 abstract protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, 063 Attributes attributes); 064 065 /** 066 * Validate preconditions of this action. 067 * 068 * By default, true is returned. Subclasses should override appropriately. 069 * 070 * @param intercon 071 * @param name 072 * @param attributes 073 * @return 074 */ 075 protected boolean validPreconditions(SaxEventInterpretationContext intercon, String name, Attributes attributes) { 076 return true; 077 } 078 079 @Override 080 public void body(SaxEventInterpretationContext ec, String body) throws ActionException { 081 if(currentModel == null) { 082 throw new ActionException("current model is null. Is <configuration> element missing?"); 083 } 084 currentModel.addText(body); 085 } 086 087 @Override 088 public void end(SaxEventInterpretationContext saxEventInterpretationContext, String name) throws ActionException { 089 if (inError) 090 return; 091 092 Model m = saxEventInterpretationContext.peekModel(); 093 094 if (m != currentModel) { 095 addWarn("The object "+ m +"] at the top of the stack differs from the model [" + currentModel.idString() 096 + "] pushed earlier."); 097 addWarn("This is wholly unexpected."); 098 } 099 100 // do not pop nor add to parent if there is no parent 101 if (parentModel != null) { 102 parentModel.addSubModel(currentModel); 103 saxEventInterpretationContext.popModel(); 104 } 105 } 106}