Skip to content

Package: ExpressionBuilder$SoftConcurrentHashMap

ExpressionBuilder$SoftConcurrentHashMap

nameinstructionbranchcomplexitylinemethod
ExpressionBuilder.SoftConcurrentHashMap()
M: 14 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
cleanup()
M: 17 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 4 C: 0
0%
M: 1 C: 0
0%
get(Object)
M: 26 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 8 C: 0
0%
M: 1 C: 0
0%
put(String, Node)
M: 23 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
putIfAbsent(String, Node)
M: 23 C: 0
0%
M: 2 C: 0
0%
M: 2 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*
2: * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
3: *
4: * This program and the accompanying materials are made available under the
5: * terms of the Eclipse Public License v. 2.0, which is available at
6: * http://www.eclipse.org/legal/epl-2.0.
7: *
8: * This Source Code may also be made available under the following Secondary
9: * Licenses when the conditions for such availability set forth in the
10: * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
11: * version 2 with the GNU Classpath Exception, which is available at
12: * https://www.gnu.org/software/classpath/license.html.
13: *
14: * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15: */
16:
17: package com.sun.el.lang;
18:
19: import java.io.StringReader;
20: import java.lang.ref.ReferenceQueue;
21: import java.lang.ref.SoftReference;
22: import java.lang.reflect.Method;
23: import java.util.concurrent.ConcurrentHashMap;
24:
25: import jakarta.el.ELContext;
26: import jakarta.el.ELException;
27: import jakarta.el.FunctionMapper;
28: import jakarta.el.MethodExpression;
29: import jakarta.el.ValueExpression;
30: import jakarta.el.VariableMapper;
31:
32: import com.sun.el.MethodExpressionImpl;
33: import com.sun.el.MethodExpressionLiteral;
34: import com.sun.el.ValueExpressionImpl;
35: import com.sun.el.parser.AstCompositeExpression;
36: import com.sun.el.parser.AstDeferredExpression;
37: import com.sun.el.parser.AstDynamicExpression;
38: import com.sun.el.parser.AstFunction;
39: import com.sun.el.parser.AstIdentifier;
40: import com.sun.el.parser.AstLiteralExpression;
41: import com.sun.el.parser.AstMethodArguments;
42: import com.sun.el.parser.AstValue;
43: import com.sun.el.parser.ELParser;
44: import com.sun.el.parser.Node;
45: import com.sun.el.parser.NodeVisitor;
46: import com.sun.el.parser.ParseException;
47: import com.sun.el.util.MessageFactory;
48:
49: /**
50: * @author Jacob Hookom [jacob@hookom.net]
51: * @author Kin-man Chung // EL cache
52: * @version $Change: 181177 $$DateTime: 2001/06/26 08:45:09 $$Author: kchung $
53: */
54: public final class ExpressionBuilder implements NodeVisitor {
55:
56: static private class NodeSoftReference extends SoftReference<Node> {
57: final String key;
58:
59: NodeSoftReference(String key, Node node, ReferenceQueue<Node> refQ) {
60: super(node, refQ);
61: this.key = key;
62: }
63: }
64:
65: static private class SoftConcurrentHashMap extends ConcurrentHashMap<String, Node> {
66:
67: private static final int CACHE_INIT_SIZE = 256;
68: private ConcurrentHashMap<String, NodeSoftReference> map = new ConcurrentHashMap<String, NodeSoftReference>(CACHE_INIT_SIZE);
69: private ReferenceQueue<Node> refQ = new ReferenceQueue<Node>();
70:
71: // Remove map entries that have been placed on the queue by GC.
72: private void cleanup() {
73: NodeSoftReference nodeRef = null;
74:• while ((nodeRef = (NodeSoftReference) refQ.poll()) != null) {
75: map.remove(nodeRef.key);
76: }
77: }
78:
79: @Override
80: public Node put(String key, Node value) {
81: cleanup();
82: NodeSoftReference prev = map.put(key, new NodeSoftReference(key, value, refQ));
83:• return prev == null ? null : prev.get();
84: }
85:
86: @Override
87: public Node putIfAbsent(String key, Node value) {
88: cleanup();
89: NodeSoftReference prev = map.putIfAbsent(key, new NodeSoftReference(key, value, refQ));
90:• return prev == null ? null : prev.get();
91: }
92:
93: @Override
94: public Node get(Object key) {
95: cleanup();
96: NodeSoftReference nodeRef = map.get(key);
97:• if (nodeRef == null) {
98: return null;
99: }
100:• if (nodeRef.get() == null) {
101: // value has been garbage collected, remove entry in map
102: map.remove(key);
103: return null;
104: }
105: return nodeRef.get();
106: }
107: }
108:
109: private static final SoftConcurrentHashMap cache = new SoftConcurrentHashMap();
110: private FunctionMapper fnMapper;
111: private VariableMapper varMapper;
112: private String expression;
113:
114: /**
115: *
116: */
117: public ExpressionBuilder(String expression, ELContext ctx) throws ELException {
118: this.expression = expression;
119:
120: FunctionMapper ctxFn = ctx.getFunctionMapper();
121: VariableMapper ctxVar = ctx.getVariableMapper();
122:
123: if (ctxFn != null) {
124: this.fnMapper = new FunctionMapperFactory(ctxFn);
125: }
126: if (ctxVar != null) {
127: this.varMapper = new VariableMapperFactory(ctxVar);
128: }
129: }
130:
131: public static Node createNode(String expr) throws ELException {
132: Node n = createNodeInternal(expr);
133: return n;
134: }
135:
136: private static Node createNodeInternal(String expr) throws ELException {
137: if (expr == null) {
138: throw new ELException(MessageFactory.get("error.null"));
139: }
140:
141: Node n = cache.get(expr);
142: if (n == null) {
143: try {
144: n = (new ELParser(
145: new com.sun.el.parser.ELParserTokenManager(new com.sun.el.parser.SimpleCharStream(new StringReader(expr), 1, 1, expr.length() + 1))))
146: .CompositeExpression();
147:
148: // validate composite expression
149: if (n instanceof AstCompositeExpression) {
150: int numChildren = n.jjtGetNumChildren();
151: if (numChildren == 1) {
152: n = n.jjtGetChild(0);
153: } else {
154: Class type = null;
155: Node child = null;
156: for (int i = 0; i < numChildren; i++) {
157: child = n.jjtGetChild(i);
158: if (child instanceof AstLiteralExpression) {
159: continue;
160: }
161: if (type == null) {
162: type = child.getClass();
163: } else {
164: if (!type.equals(child.getClass())) {
165: throw new ELException(MessageFactory.get("error.mixed", expr));
166: }
167: }
168: }
169: }
170: }
171: if (n instanceof AstDeferredExpression || n instanceof AstDynamicExpression) {
172: n = n.jjtGetChild(0);
173: }
174: cache.putIfAbsent(expr, n);
175: } catch (ParseException pe) {
176: throw new ELException("Error Parsing: " + expr, pe);
177: }
178: }
179: return n;
180: }
181:
182: /**
183: * Scan the expression nodes and captures the functions and variables used in this expression. This ensures that any
184: * changes to the functions or variables mappings during the expression will not affect the evaluation of this
185: * expression, as the functions and variables are bound and resolved at parse time, as specified in the spec.
186: */
187: private void prepare(Node node) throws ELException {
188: node.accept(this);
189: if (this.fnMapper instanceof FunctionMapperFactory) {
190: this.fnMapper = ((FunctionMapperFactory) this.fnMapper).create();
191: }
192: if (this.varMapper instanceof VariableMapperFactory) {
193: this.varMapper = ((VariableMapperFactory) this.varMapper).create();
194: }
195: }
196:
197: private Node build() throws ELException {
198: Node n = createNodeInternal(this.expression);
199: this.prepare(n);
200: if (n instanceof AstDeferredExpression || n instanceof AstDynamicExpression) {
201: n = n.jjtGetChild(0);
202: }
203: return n;
204: }
205:
206: /*
207: * (non-Javadoc)
208: *
209: * @see com.sun.el.parser.NodeVisitor#visit(com.sun.el.parser.Node)
210: */
211: @Override
212: public void visit(Node node) throws ELException {
213: if (node instanceof AstFunction) {
214: AstFunction funcNode = (AstFunction) node;
215: if ((funcNode.getPrefix().length() == 0)
216: && (this.fnMapper == null || fnMapper.resolveFunction(funcNode.getPrefix(), funcNode.getLocalName()) == null)) {
217: // This can be a call to a LambdaExpression. The target
218: // of the call is a bean or an Jakarta Expression variable. Capture
219: // the variable name in the variable mapper if it is an
220: // variable. The decision to invoke the static method or
221: // the LambdaExpression will be made at runtime.
222: if (this.varMapper != null) {
223: this.varMapper.resolveVariable(funcNode.getLocalName());
224: }
225: return;
226: }
227:
228: if (this.fnMapper == null) {
229: throw new ELException(MessageFactory.get("error.fnMapper.null"));
230: }
231: Method m = fnMapper.resolveFunction(funcNode.getPrefix(), funcNode.getLocalName());
232: if (m == null) {
233: throw new ELException(MessageFactory.get("error.fnMapper.method", funcNode.getOutputName()));
234: }
235: int pcnt = m.getParameterTypes().length;
236: int acnt = ((AstMethodArguments) node.jjtGetChild(0)).getParameterCount();
237: if (acnt != pcnt) {
238: throw new ELException(MessageFactory.get("error.fnMapper.paramcount", funcNode.getOutputName(), "" + pcnt, "" + acnt));
239: }
240: } else if (node instanceof AstIdentifier && this.varMapper != null) {
241: String variable = ((AstIdentifier) node).getImage();
242:
243: // simply capture it
244: this.varMapper.resolveVariable(variable);
245: }
246: }
247:
248: public ValueExpression createValueExpression(Class expectedType) throws ELException {
249: Node n = this.build();
250: return new ValueExpressionImpl(this.expression, n, this.fnMapper, this.varMapper, expectedType);
251: }
252:
253: public MethodExpression createMethodExpression(Class expectedReturnType, Class[] expectedParamTypes) throws ELException {
254: Node n = this.build();
255: if (n instanceof AstValue || n instanceof AstIdentifier) {
256: return new MethodExpressionImpl(expression, n, this.fnMapper, this.varMapper, expectedReturnType, expectedParamTypes);
257: } else if (n instanceof AstLiteralExpression) {
258: return new MethodExpressionLiteral(expression, expectedReturnType, expectedParamTypes);
259: } else {
260: throw new ELException("Not a Valid Method Expression: " + expression);
261: }
262: }
263: }