View Javadoc
1   /*
2    *    Copyright 2009-2023 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       https://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.scripting.xmltags;
17  
18  import java.util.HashMap;
19  import java.util.Map;
20  import java.util.StringJoiner;
21  
22  import ognl.OgnlContext;
23  import ognl.OgnlRuntime;
24  import ognl.PropertyAccessor;
25  
26  import org.apache.ibatis.reflection.MetaObject;
27  import org.apache.ibatis.session.Configuration;
28  
29  /**
30   * @author Clinton Begin
31   */
32  public class DynamicContext {
33  
34    public static final String PARAMETER_OBJECT_KEY = "_parameter";
35    public static final String DATABASE_ID_KEY = "_databaseId";
36  
37    static {
38      OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor());
39    }
40  
41    private final ContextMap bindings;
42    private final StringJoiner sqlBuilder = new StringJoiner(" ");
43    private int uniqueNumber;
44  
45    public DynamicContext(Configuration configuration, Object parameterObject) {
46      if (parameterObject != null && !(parameterObject instanceof Map)) {
47        MetaObject metaObject = configuration.newMetaObject(parameterObject);
48        boolean existsTypeHandler = configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass());
49        bindings = new ContextMap(metaObject, existsTypeHandler);
50      } else {
51        bindings = new ContextMap(null, false);
52      }
53      bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
54      bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
55    }
56  
57    public Map<String, Object> getBindings() {
58      return bindings;
59    }
60  
61    public void bind(String name, Object value) {
62      bindings.put(name, value);
63    }
64  
65    public void appendSql(String sql) {
66      sqlBuilder.add(sql);
67    }
68  
69    public String getSql() {
70      return sqlBuilder.toString().trim();
71    }
72  
73    public int getUniqueNumber() {
74      return uniqueNumber++;
75    }
76  
77    static class ContextMap extends HashMap<String, Object> {
78      private static final long serialVersionUID = 2977601501966151582L;
79      private final MetaObject parameterMetaObject;
80      private final boolean fallbackParameterObject;
81  
82      public ContextMap(MetaObject parameterMetaObject, boolean fallbackParameterObject) {
83        this.parameterMetaObject = parameterMetaObject;
84        this.fallbackParameterObject = fallbackParameterObject;
85      }
86  
87      @Override
88      public Object get(Object key) {
89        String strKey = (String) key;
90        if (super.containsKey(strKey)) {
91          return super.get(strKey);
92        }
93  
94        if (parameterMetaObject == null) {
95          return null;
96        }
97  
98        if (fallbackParameterObject && !parameterMetaObject.hasGetter(strKey)) {
99          return parameterMetaObject.getOriginalObject();
100       }
101       // issue #61 do not modify the context when reading
102       return parameterMetaObject.getValue(strKey);
103     }
104   }
105 
106   static class ContextAccessor implements PropertyAccessor {
107 
108     @Override
109     public Object getProperty(OgnlContext context, Object target, Object name) {
110       Map map = (Map) target;
111 
112       Object result = map.get(name);
113       if (map.containsKey(name) || result != null) {
114         return result;
115       }
116 
117       Object parameterObject = map.get(PARAMETER_OBJECT_KEY);
118       if (parameterObject instanceof Map) {
119         return ((Map) parameterObject).get(name);
120       }
121 
122       return null;
123     }
124 
125     @Override
126     public void setProperty(OgnlContext context, Object target, Object name, Object value) {
127       Map<Object, Object> map = (Map<Object, Object>) target;
128       map.put(name, value);
129     }
130 
131     @Override
132     public String getSourceAccessor(OgnlContext arg0, Object arg1, Object arg2) {
133       return null;
134     }
135 
136     @Override
137     public String getSourceSetter(OgnlContext arg0, Object arg1, Object arg2) {
138       return null;
139     }
140   }
141 }