protected Object resolveNamespace()

in src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java [815:920]


    protected Object resolveNamespace(final String prefix, final JexlNode node) {
        Object namespace;
        // check whether this namespace is a functor
        synchronized (this) {
            if (functors != null) {
                namespace = functors.get(prefix);
                if (namespace != null) {
                    return namespace;
                }
            }
        }
        // check if namespace is a resolver
        namespace = ns.resolveNamespace(prefix);
        if (namespace == null) {
            namespace = functions.get(prefix);
            if (namespace == null) {
                namespace = jexl.getNamespace(prefix);
            }
            if (prefix != null && namespace == null) {
                throw new JexlException(node, "no such function namespace " + prefix, null);
            }
        }
        Object functor = null;
        // class or string (*1)
        if (namespace instanceof Class<?> || namespace instanceof String) {
            // the namespace(d) identifier
            final ASTIdentifier nsNode = (ASTIdentifier) node.jjtGetChild(0);
            final boolean cacheable = cache && prefix != null;
            final Object cached = cacheable ? nsNode.jjtGetValue() : null;
            // we know the class is used as namespace of static methods, no functor
            if (cached instanceof Class<?>) {
                return cached;
            }
            // attempt to reuse last cached constructor
            if (cached instanceof JexlContext.NamespaceFunctor) {
                final Object eval = ((JexlContext.NamespaceFunctor) cached).createFunctor(context);
                if (JexlEngine.TRY_FAILED != eval) {
                    functor = eval;
                    namespace = cached;
                }
            }
            if (functor == null) {
                // find a constructor with that context as argument or without
                for (int tried = 0; tried < 2; ++tried) {
                    final boolean withContext = tried == 0;
                    final JexlMethod ctor = withContext
                            ? uberspect.getConstructor(namespace, context)
                            : uberspect.getConstructor(namespace);
                    if (ctor != null) {
                        try {
                            functor = withContext
                                    ? ctor.invoke(namespace, context)
                                    : ctor.invoke(namespace);
                            // defensive
                            if (functor != null) {
                                // wrap the namespace in a NamespaceFunctor to shield us from the actual
                                // number of arguments to call it with.
                                final Object nsFinal = namespace;
                                // make it a class (not a lambda!) so instanceof (see *2) will catch it
                                namespace = (NamespaceFunctor) context -> withContext
                                        ? ctor.tryInvoke(null, nsFinal, context)
                                        : ctor.tryInvoke(null, nsFinal);
                                if (cacheable && ctor.isCacheable()) {
                                    nsNode.jjtSetValue(namespace);
                                }
                                break; // we found a constructor that did create a functor
                            }
                        } catch (final Exception xinst) {
                            throw new JexlException(node, "unable to instantiate namespace " + prefix, xinst);
                        }
                    }
                }
                // did not, will not create a functor instance; use a class, namespace of static methods
                if (functor == null) {
                    try {
                        // try to find a class with that name
                        if (namespace instanceof String) {
                            namespace = uberspect.getClassLoader().loadClass((String) namespace);
                        }
                        // we know it's a class in all cases (see *1)
                        if (cacheable) {
                            nsNode.jjtSetValue(namespace);
                        }
                    } catch (final ClassNotFoundException e) {
                        // not a class
                        throw new JexlException(node, "no such class namespace " + prefix, e);
                    }
                }
            }
        }
        // if a namespace functor, instantiate the functor (if not done already) and store it (*2)
        if (functor == null && namespace instanceof JexlContext.NamespaceFunctor) {
            functor = ((JexlContext.NamespaceFunctor) namespace).createFunctor(context);
        }
        // got a functor, store it and return it
        if (functor != null) {
            synchronized (this) {
                if (functors == null) {
                    functors = new HashMap<>();
                }
                functors.put(prefix, functor);
            }
            return functor;
        }
        return namespace;
    }