All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.coherence.dslquery.internal.AbstractCoherenceQueryWalker Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2020, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 */
package com.tangosol.coherence.dslquery.internal;

import com.tangosol.coherence.config.ResolvableParameterList;
import com.tangosol.coherence.config.SimpleParameterList;

import com.tangosol.coherence.config.builder.ParameterizedBuilder;

import com.tangosol.coherence.dslquery.CoherenceQueryLanguage;
import com.tangosol.coherence.dslquery.ExtractorBuilder;

import com.tangosol.coherence.dslquery.function.FunctionBuilders;

import com.tangosol.coherence.dsltools.termtrees.AtomicTerm;
import com.tangosol.coherence.dsltools.termtrees.NodeTerm;
import com.tangosol.coherence.dsltools.termtrees.Term;
import com.tangosol.coherence.dsltools.termtrees.TermWalker;
import com.tangosol.coherence.dsltools.termtrees.Terms;

import com.tangosol.config.expression.NullParameterResolver;
import com.tangosol.config.expression.Parameter;
import com.tangosol.config.expression.ParameterResolver;

import com.tangosol.util.Base;
import com.tangosol.util.ClassHelper;
import com.tangosol.util.ValueExtractor;

import com.tangosol.util.extractor.AbstractExtractor;
import com.tangosol.util.extractor.ChainedExtractor;
import com.tangosol.util.extractor.KeyExtractor;
import com.tangosol.util.extractor.ReflectionExtractor;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * AbstractCoherenceTermWalker is a visitor class that provides a framework
 * for walking Term Trees by providing classification methods based on the
 * Abstract Syntax Tree vocabulary for the Coherence Query expression
 * Language. These classification methods are passed values extracted from the
 * AST so tht they may remain ignorant of the AST to Term tree representation.
 * Subclasses may ignore classifications for which they are not
 * interested.
 *
 * @author djl  2009.08.31
 * @author jk   2013.12.02
 */
public abstract class AbstractCoherenceQueryWalker
        implements TermWalker
    {
    // ----- constructors ---------------------------------------------------

    /**
     * Creates a AbstractCoherenceQueryWalker with the specified
     * bind variables.
     *
     * @param listBindVars   the indexed ind variables
     * @param namedBindVars  the named bind variables
     * @param language       the {@link CoherenceQueryLanguage} instance to use
     */
    protected AbstractCoherenceQueryWalker(List listBindVars, ParameterResolver namedBindVars,
                                           CoherenceQueryLanguage language)
        {
        m_listBindVars = new ArrayList<>();

        if (listBindVars != null)
            {
            m_listBindVars.addAll(listBindVars);
            }

        if (namedBindVars == null)
            {
            namedBindVars = new NullParameterResolver();
            }

        m_namedBindVars   = namedBindVars;
        f_language        = language;
        f_propertyBuilder = new PropertyBuilder();
        f_termKeyFunction = Terms.create("callNode(key())", language);
        }

    // ----- TermWalker API -------------------------------------------------

    @Override
    public void acceptNode(String sFunctor, NodeTerm term)
        {
        String     sBindingType;
        AtomicTerm atomicTerm;
        String     sAlias = m_sAlias;

        switch (sFunctor)
            {
            case "literal" :
                acceptLiteral((AtomicTerm) term.termAt(1));
                break;

            case "listNode" :
                acceptList(term);
                break;

            case "identifier" :
                if (sAlias != null)
                    {
                    String sid = ((AtomicTerm) term.termAt(1)).getValue();

                    if (sAlias.equals(sid))
                        {
                        acceptCall("value", new NodeTerm("value"));

                        return;
                        }
                    }

                acceptIdentifier(((AtomicTerm) term.termAt(1)).getValue());
                break;

            case "binaryOperatorNode" :
                acceptBinaryOperator(((AtomicTerm) term.termAt(1)).getValue(), term.termAt(2), term.termAt(3));
                break;

            case "unaryOperatorNode" :
                acceptUnaryOperator(((AtomicTerm) term.termAt(1)).getValue(), term.termAt(2));
                break;

            case "bindingNode" :
                sBindingType = ((AtomicTerm) term.termAt(1)).getValue();
                atomicTerm   = (AtomicTerm) term.termAt(2).termAt(1);

                if ("?".equals(sBindingType))
                    {
                    acceptNumericBinding(atomicTerm.getNumber().intValue());
                    }
                else
                    {
                    acceptKeyedBinding(atomicTerm.getValue());
                    }

                break;

            case "callNode" :
                acceptCall((term.termAt(1)).getFunctor(), (NodeTerm) term.termAt(1));
                break;

            case "derefNode" :
                if (sAlias != null)
                    {
                    Term termChild = term.termAt(1);

                    if ("identifier".equals(termChild.getFunctor()))
                        {
                        atomicTerm = (AtomicTerm) termChild.termAt(1);

                        if (sAlias.equals(atomicTerm.getValue()))
                            {
                            int nTerms = term.length() - 1;

                            if (nTerms == 1)
                                {
                                Term t2 = term.termAt(2);

                                t2.accept(this);

                                return;
                                }

                            Term[] aTerms = new Term[term.length() - 1];

                            System.arraycopy(term.children(), 1, aTerms, 0, term.length() - 1);
                            acceptPath(new NodeTerm(sFunctor, aTerms));

                            return;
                            }
                        }
                    }

                acceptPath(term);
                break;

            default :
                throw new RuntimeException("Unknown AST node: " + term.fullFormString());
            }
        }

    @Override
    public void acceptAtom(String sFunctor, AtomicTerm atomicTerm)
        {
        m_oResult    = atomicTerm.getObject();
        m_atomicTerm = atomicTerm;
        }

    @Override
    public void acceptTerm(String sFunctor, Term term)
        {
        }

    @Override
    public Object walk(Term term)
        {
        term.accept(this);

        return getResult();
        }

    // ----- AbstractCoherenceQueryWalker API ---------------------------------------

    /**
     * The receiver has classified a literal node.
     *
     * @param atom  the term representing the literal
     */
    protected void acceptLiteral(AtomicTerm atom)
        {
        m_oResult    = atom.getObject();
        m_atomicTerm = atom;
        }

    /**
     * The receiver has classified a list node.
     *
     * @param termList  the Term whose children represent the elements of the list
     */
    protected void acceptList(NodeTerm termList)
        {
        }

    /**
     * The receiver has classified an identifier node.
     *
     * @param sIdentifier  the String representing the identifier
     */
    protected void acceptIdentifier(String sIdentifier)
        {
        }

    /**
     * Return true if the identifier specified is a well known identifier
     * ('null', 'true' or 'false'), with a side-affect of {@code m_oResult}
     * being set appropriately.
     *
     * @param sIdentifier  the identifier to accept
     *
     * @return true if the identifier was a well known value otherwise false
     */
    protected boolean acceptIdentifierInternal(String sIdentifier)
        {
        switch (sIdentifier.toLowerCase())
            {
            case "null" :
                m_oResult = null;

                return true;

            case "true" :
                m_oResult = Boolean.TRUE;

                return true;

            case "false" :
                m_oResult = Boolean.FALSE;

                return true;
            }

        return false;
        }

    /**
     * The receiver has classified a binary operation node.
     *
     * @param sOperator  the string representing the operator
     * @param termLeft   the left Term of the operation
     * @param termRight  the right Term of the operation
     */
    protected void acceptBinaryOperator(String sOperator, Term termLeft, Term termRight)
        {
        }

    /**
     * The receiver has classified a unary operation node.
     *
     * @param sOperator  the string representing the operator
     * @param term       the Term being operated upon
     */
    protected void acceptUnaryOperator(String sOperator, Term term)
        {
        }

    /**
     * The receiver has classified a bind slot.
     *
     * @param iVar  the 1-based index into the bind variables
     */
    protected void acceptNumericBinding(int iVar)
        {
        m_oResult = m_listBindVars.get(iVar - 1);
        }

    /**
     * The receiver has classified a bind slot.
     *
     * @param sName  the name of the bind variable to use
     */
    protected void acceptKeyedBinding(String sName)
        {
        Parameter p = m_namedBindVars.resolve(sName);
        if (p == null)
            {
            throw new RuntimeException("Unable to resolve named bind variable: " + sName);
            }
        else
            {
            m_oResult = p.evaluate(m_namedBindVars).get();
            }
        }

    /**
     * The receiver has classified a call node.
     *
     * @param sFunctionName  the function name
     * @param term           a Term whose children are the parameters to the call
     */
    protected void acceptCall(String sFunctionName, NodeTerm term)
        {
        ParameterizedBuilder functionBuilder = f_language.getFunction(sFunctionName);

        if (functionBuilder == null)
            {
            functionBuilder = FunctionBuilders.METHOD_CALL_FUNCTION_BUILDER;
            }

        ResolvableParameterList resolver       = new ResolvableParameterList();
        SimpleParameterList     listParameters = new SimpleParameterList();

        resolver.add(new Parameter("functionName", sFunctionName));

        for (int i = 1, cTerms = term.length(); i <= cTerms; i++)
            {
            term.termAt(i).accept(this);
            listParameters.add(m_oResult);
            }

        m_oResult = functionBuilder.realize(resolver, null, listParameters);
        }

    /**
     * The receiver has classified a path node.
     *
     * @param term  a Term whose children are the elements of the path
     */
    protected void acceptPath(NodeTerm term)
        {
        }

    /**
     * Process the specified path term as a {@link ChainedExtractor}.
     *
     * @param sCacheName  the cache name the extractor will be executed on
     * @param nodeTerm    the {@link NodeTerm} containing the path to use
     *                    to build the ChainedExtractor
     */
    protected void acceptPathAsChainedExtractor(String sCacheName, NodeTerm nodeTerm)
        {
        List listExtractors = new ArrayList<>();
        StringBuilder        sbPath         = new StringBuilder();
        int                  nTarget        = AbstractExtractor.VALUE;
        boolean              fIdentifier    = true;

        for (Term term : nodeTerm)
            {
            if (f_termKeyFunction.termEqual(term))
               {
               nTarget = AbstractExtractor.KEY;
               continue;
               }

            fIdentifier = fIdentifier && "identifier".equals(term.getFunctor());

            if (fIdentifier)
                {
                if (sbPath.length() > 0)
                    {
                    sbPath.append(".");
                    }

                sbPath.append(((AtomicTerm) term.termAt(1)).getValue());
                }
            else
                {
                term.accept(this);
                listExtractors.add((ValueExtractor) getResult());
                }
            }

        if (sbPath.length() == 0 && nTarget == AbstractExtractor.KEY)
            {
            // The target is KEY and there were no identifiers in the NodeTerm tree
            // so insert a KeyExtractor at the front of the chain
            listExtractors.add(0, new KeyExtractor());
            }
        else if (sbPath.length() > 0)
            {
            // Build a chain of ValueExtractors from the sbPath property list
            ExtractorBuilder builder   = f_language.getExtractorBuilder();
            ValueExtractor   extractor = builder.realize(sCacheName, nTarget, sbPath.toString());

            listExtractors.add(0, extractor);
            }

        m_oResult = buildExtractor(listExtractors);
        }

    /**
     * Create a single {@link ValueExtractor} from the {@link List} of
     * ValueExtractors.
     * 

* If the List contains a single ValueExtractor then that is returned * from this method. If the List contains multiple ValueExtractors * then these are combined into a {@link ChainedExtractor}. * * @param listExtractors the List of ValueExtractors to use * * @return a single ValueExtractor built from the List of ValueExtractors */ protected ValueExtractor buildExtractor(List listExtractors) { if (listExtractors.size() == 1) { return listExtractors.get(0); } List list = new ArrayList<>(); for (ValueExtractor extractor : listExtractors) { if (extractor instanceof ChainedExtractor) { list.addAll(Arrays.asList(((ChainedExtractor) extractor).getExtractors())); } else { list.add(extractor); } } return new ChainedExtractor(list.toArray(new ValueExtractor[list.size()])); } // ----- accessors ----------------------------------------------------- /** * Set the flag that controls whether to process an "Extended Language" statement. * * @param fExtendedLanguage flag that determines whether to process * an extended language */ public void setExtendedLanguage(boolean fExtendedLanguage) { m_fExtendedLanguage = fExtendedLanguage; } /** * Set the value for the result object. * * @param oResult the value to set as the result */ public void setResult(Object oResult) { m_oResult = oResult; } @Override public Object getResult() { return m_oResult; } // ----- helper methods ------------------------------------------------ /** * Use reflection to make Object either my calling a constructor or * static method. * * @param fUseNew flag that controls whether to use constructor * @param oExtractor the ReflectionExtractor or array of ReflectionExtractors * * @return the constructed object */ protected Object reflectiveMakeObject(boolean fUseNew, Object oExtractor) { String sName = null; StringBuilder sbPath = new StringBuilder(); ReflectionExtractor extractor = null; Class cls; if (oExtractor instanceof ReflectionExtractor) { extractor = (ReflectionExtractor) oExtractor; } else if (oExtractor instanceof ChainedExtractor) { ChainedExtractor extractorChained = (ChainedExtractor) oExtractor; ValueExtractor[] aExtractors = extractorChained.getExtractors(); for (int i = 0; i < aExtractors.length - 1; i++) { ReflectionExtractor reflectionExtractor = (ReflectionExtractor) aExtractors[i]; if (sbPath.length() > 0) { sbPath.append('.'); } String sMethodName = reflectionExtractor.getMethodName(); sbPath.append(f_propertyBuilder.plainName(sMethodName)); } extractor = (ReflectionExtractor) aExtractors[aExtractors.length - 1]; } else if (oExtractor instanceof Object[]) { Object[] ao = (Object[]) oExtractor; for (int i = 0; i < ao.length - 1; ++i) { if (sbPath.length() > 0) { sbPath.append('.'); } sbPath.append(ao[i]); } extractor = (ReflectionExtractor) ao[ao.length - 1]; } String sMethod = extractor.getMethodName(); Object[] aoArgs = extractor.getParameters(); try { if (fUseNew) { sName = sbPath.length() > 0 ? sbPath + "." + sMethod : sMethod; cls = Class.forName(sName); return ClassHelper.newInstance(cls, aoArgs); } else if (m_fExtendedLanguage && sMethod.equals(".object.")) { sName = sbPath.length() > 0 ? sbPath + "." + aoArgs[0] : (String) aoArgs[0]; cls = Class.forName(sName); Object oInstance = ClassHelper.newInstance(cls, new Object[0]); if (aoArgs.length != 2) { throw new RuntimeException("Malformed object creation " + sName); } if (aoArgs[1] instanceof Map) { Map map = (Map) aoArgs[1]; for (Map.Entry e : map.entrySet()) { String setter = f_propertyBuilder.updaterStringFor((String) e.getKey()); ClassHelper.invoke(cls, oInstance, setter, new Object[] {e.getValue()}); } } return oInstance; } else { if (sbPath.length() == 0) { throw new RuntimeException("Malformed static call " + sMethod); } sName = sbPath.toString(); cls = Class.forName(sName); return ClassHelper.invokeStatic(cls, sMethod, aoArgs); } } catch (InstantiationException e) { StringBuilder sb = new StringBuilder(" Unable to instantiate ").append(sName).append(" with "); append(sb, aoArgs, ", "); throw Base.ensureRuntimeException(e, sb.toString()); } catch (NoSuchMethodException e) { StringBuilder sb = new StringBuilder("Unable to find method ").append(sMethod).append(" on ").append(sName) .append(" with: "); append(sb, aoArgs, ", "); throw Base.ensureRuntimeException(e, sb.toString()); } catch (Exception e) { throw Base.ensureRuntimeException(e); } } /** * Append each of the values in the aoObjects array to the {@link StringBuilder} * appending the specified separator between each value. * * @param sb the StringBuilder to append the values to * @param aoObjects the values to append to the StringBuilder * @param sSeparator the separator to use */ protected void append(StringBuilder sb, Object[] aoObjects, String sSeparator) { boolean fFirst = true; for (Object o : aoObjects) { if (!fFirst) { sb.append(sSeparator); fFirst = false; } sb.append(o); } } // ----- accessors ------------------------------------------------------ /** * Set the alias that was used in naming caches. Allowed to be used in * path expressions. * * @param sAlias The String that is the alias */ public void setAlias(String sAlias) { m_sAlias = sAlias; } // ----- data members --------------------------------------------------- /** * The instance of {@link CoherenceQueryLanguage} to use. */ protected final CoherenceQueryLanguage f_language; /** * The alias of the cache name being used. */ protected String m_sAlias = null; /** * An Object that is the result of each classified dispatch as the * tree of Objects is built */ protected Object m_oResult; /** * The AtomicTerm that was last processed */ protected AtomicTerm m_atomicTerm; /** * The PropertyBuilder used to make getters and setters */ protected final PropertyBuilder f_propertyBuilder; /** * Flag that controls whether we except Map, and List as literals. * This gives a json like feel to the language. */ protected boolean m_fExtendedLanguage = false; /** * The current indexed list of bind variables. */ protected List m_listBindVars; /** * The current named bind variables. */ protected ParameterResolver m_namedBindVars; /** * Constant equal to an empty Key function term. */ protected final Term f_termKeyFunction; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy