1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v2.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.core.joran.action;
15  
16  import org.xml.sax.Attributes;
17  
18  import ch.qos.logback.core.joran.spi.ActionException;
19  import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext;
20  import ch.qos.logback.core.model.Model;
21  
22  public abstract class BaseModelAction extends Action {
23  
24      Model parentModel;
25      Model currentModel;
26      boolean inError = false;
27  
28      @Override
29      public void begin(SaxEventInterpretationContext saxEventInterpretationContext, String name, Attributes attributes)
30              throws ActionException {
31          parentModel = null;
32          inError = false;
33  
34          if (!validPreconditions(saxEventInterpretationContext, name, attributes)) {
35              inError = true;
36              return;
37          }
38  
39          currentModel = buildCurrentModel(saxEventInterpretationContext, name, attributes);
40          currentModel.setTag(name);
41          if (!saxEventInterpretationContext.isModelStackEmpty()) {
42              parentModel = saxEventInterpretationContext.peekModel();
43          }
44          final int lineNumber = getLineNumber(saxEventInterpretationContext);
45          currentModel.setLineNumber(lineNumber);
46          saxEventInterpretationContext.pushModel(currentModel);
47      }
48  
49      /**
50       * Builds and returns a Model instance for the current XML element being processed.
51       *
52       * <p>This method is called during the begin phase of XML processing to create a Model
53       * object that represents the current element. The returned model will be configured with
54       * the element's tag name, line number, and will be pushed onto the model stack.</p>
55       *
56       * @param interpretationContext the context for interpreting SAX events, providing access to
57       *                              the model stack and other interpretation state
58       * @param name the name of the XML element being processed
59       * @param attributes the attributes of the XML element
60       * @return a new Model instance representing the current XML element
61       */
62      abstract protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name,
63              Attributes attributes);
64  
65      /**
66       * Validate preconditions of this action.
67       * 
68       * By default, true is returned. Subclasses should override appropriately.
69       * 
70       * @param intercon
71       * @param name
72       * @param attributes
73       * @return
74       */
75      protected boolean validPreconditions(SaxEventInterpretationContext intercon, String name, Attributes attributes) {
76          return true;
77      }
78  
79      @Override
80      public void body(SaxEventInterpretationContext ec, String body) throws ActionException {
81          if(currentModel == null) {
82              throw new ActionException("current model is null. Is <configuration> element missing?");
83          }
84          currentModel.addText(body);
85      }
86  
87      @Override
88      public void end(SaxEventInterpretationContext saxEventInterpretationContext, String name) throws ActionException {
89          if (inError)
90              return;
91  
92          Model m = saxEventInterpretationContext.peekModel();
93  
94          if (m != currentModel) {
95              addWarn("The object "+ m +"] at the top of the stack differs from the model [" + currentModel.idString()
96                      + "] pushed earlier.");
97              addWarn("This is wholly unexpected.");
98          }
99  
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 }