1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2025, 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 v1.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.model.processor.conditional;
15  
16  import ch.qos.logback.core.util.EnvUtil;
17  import ch.qos.logback.core.util.OptionHelper;
18  import ch.qos.logback.core.Context;
19  import ch.qos.logback.core.CoreConstants;
20  import ch.qos.logback.core.joran.conditional.Condition;
21  import ch.qos.logback.core.joran.conditional.PropertyEvalScriptBuilder;
22  import ch.qos.logback.core.model.Model;
23  import ch.qos.logback.core.model.conditional.IfModel;
24  import ch.qos.logback.core.model.conditional.IfModel.BranchState;
25  import ch.qos.logback.core.model.processor.ModelHandlerBase;
26  import ch.qos.logback.core.model.processor.ModelHandlerException;
27  import ch.qos.logback.core.model.processor.ModelInterpretationContext;
28  import ch.qos.logback.core.spi.ScanException;
29  
30  public class IfModelHandler extends ModelHandlerBase {
31  
32  
33      public static final String MISSING_JANINO_MSG = "Could not find Janino library on the class path. Skipping conditional processing.";
34      public static final String MISSING_JANINO_SEE = "See also " + CoreConstants.CODES_URL + "#ifJanino";
35  
36      public static final String NEW_OPERATOR_DISALLOWED_MSG = "The 'condition' attribute may not contain the 'new' operator.";
37      public static final String NEW_OPERATOR_DISALLOWED_SEE = "See also " + CoreConstants.CODES_URL + "#conditionNew";
38  
39      public static final String CONDITION_ATTR_DEPRECATED_MSG = "The 'condition' attribute in <if> element is deprecated and slated for removal. Use <condition> element instead.";
40      public static final String CONDITION_ATTR_DEPRECATED_SEE = "See also " + CoreConstants.CODES_URL + "#conditionAttributeDeprecation";
41  
42      enum Branch {IF_BRANCH, ELSE_BRANCH; }
43      
44      IfModel ifModel = null;
45      
46      public IfModelHandler(Context context) {
47          super(context);
48      }
49  
50      static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) {
51          return new IfModelHandler(context);
52      }
53  
54      @Override
55      protected Class<IfModel> getSupportedModelClass() {
56          return IfModel.class;
57      }
58      
59      @Override
60      public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException {
61          
62          ifModel = (IfModel) model;
63          mic.pushModel(ifModel);
64          Object micTopObject = mic.peekObject();
65          String conditionStr = ifModel.getCondition();
66          emitDeprecationWarningIfNecessary(conditionStr);
67  
68  
69          if(micTopObject instanceof BranchState) {
70              BranchState branchState = (BranchState) micTopObject;
71              ifModel.setBranchState(branchState);
72              // consume the BranchState at top of the object stack
73              mic.popObject();
74          } else {
75              janinoFallback(mic, model, conditionStr);
76          }
77      }
78  
79      private void janinoFallback(ModelInterpretationContext mic, Model model, String conditionStr) {
80          if (!EnvUtil.isJaninoAvailable()) {
81              addError(MISSING_JANINO_MSG);
82              addError(MISSING_JANINO_SEE);
83              return;
84          }
85  
86          Condition condition = null;
87          int lineNum = model.getLineNumber();
88  
89          if (!OptionHelper.isNullOrEmptyOrAllSpaces(conditionStr)) {
90              try {
91                  conditionStr = OptionHelper.substVars(conditionStr, mic, context);
92              } catch (ScanException e) {
93                 addError("Failed to parse input [" + conditionStr + "] on line "+lineNum, e);
94                 ifModel.setBranchState(BranchState.IN_ERROR);
95                  return;
96              }
97  
98              // do not allow 'new' operator
99              if(hasNew(conditionStr)) {
100                 addError(NEW_OPERATOR_DISALLOWED_MSG);
101                 addError(NEW_OPERATOR_DISALLOWED_SEE);
102                 return;
103             }
104 
105             try {
106                 PropertyEvalScriptBuilder pesb = new PropertyEvalScriptBuilder(mic);
107                 pesb.setContext(context);
108                 condition = pesb.build(conditionStr);
109             } catch (Exception|NoClassDefFoundError e) {
110                 ifModel.setBranchState(BranchState.IN_ERROR);
111                 addError("Failed to parse condition [" + conditionStr + "] on line "+lineNum, e);
112                 return;
113             }
114 
115             if (condition != null) {
116                 boolean boolResult = condition.evaluate();
117                 addInfo("Condition ["+conditionStr+"] evaluated to "+boolResult+ " on line "+lineNum);
118                 ifModel.setBranchState(boolResult);
119             } else {
120                 addError("The condition variable is null. This should not occur.");
121                 ifModel.setBranchState(BranchState.IN_ERROR);
122                 return;
123             }
124         }
125     }
126 
127     private void emitDeprecationWarningIfNecessary(String conditionStr) {
128         if(!OptionHelper.isNullOrEmptyOrAllSpaces(conditionStr)) {
129             addWarn(CONDITION_ATTR_DEPRECATED_MSG);
130             addWarn(CONDITION_ATTR_DEPRECATED_SEE);
131         }
132     }
133 
134 
135     private boolean hasNew(String conditionStr) {
136         return conditionStr.contains("new ");
137     }
138 
139     @Override
140     public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException {
141 
142         if(mic.isModelStackEmpty()) {
143             addError("Unexpected unexpected empty model stack.");
144             return;
145         }
146 
147         Object o = mic.peekModel();
148         if (o != ifModel) {
149             addWarn("The object [" + o + "] on the top the of the stack is not the expected [" + ifModel);
150         } else {
151             mic.popModel();
152         }
153     }
154 
155 }