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}