Package org.apache.tapestry.binding

Source Code of org.apache.tapestry.binding.ExpressionBinding

/* $$ Clover has instrumented this file $$ */// Copyright 2004 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.apache.tapestry.binding;

import java.util.Map;

import ognl.Ognl;
import ognl.OgnlException;
import ognl.TypeConverter;

import org.apache.hivemind.ClassResolver;
import org.apache.hivemind.Location;
import org.apache.tapestry.BindingException;
import org.apache.tapestry.IComponent;
import org.apache.tapestry.Tapestry;
import org.apache.tapestry.spec.BeanLifecycle;
import org.apache.tapestry.spec.IBeanSpecification;
import org.apache.tapestry.spec.IApplicationSpecification;
import org.apache.tapestry.util.StringSplitter;
import org.apache.tapestry.util.prop.OgnlUtils;

/**
*  Implements a dynamic binding, based on getting and fetching
*  values using JavaBeans property access.  This is built
*  upon the <a href="http://www.ognl.org">OGNL</a> library.
*
<p><b>Optimization of the Expression</b>
*
<p>There's a lot of room for optimization here because we can
*  count on some portions of the expression to be
*  effectively static.  Note that we type the root object as
{@link IComponent}.  We have some expectations that
*  certain properties of the root (and properties reachable from the root)
*  will be constant for the lifetime of the binding.  For example,
*  components never change thier page or container.  This means
*  that certain property prefixes can be optimized:
*
<ul>
<li>page
<li>container
<li>components.<i>name</i>
</ul>
*
<p>This means that once an ExpressionBinding has been triggered,
*  the {@link #toString()} method may return different values for the root
*  component and the expression than was originally set.
*
<p><b>Identifying Invariants</b>
*
<p>Most expressions are fully dynamic; they must be
*  resolved each time they are accessed.  This can be somewhat inefficient.
*  Tapestry can identify certain paths as invariant:
*
<ul>
<li>A component within the page hierarchy
<li>An {@link org.apache.tapestry.IAsset} from then assets map (property <code>assets</code>)
<li>A {@link org.apache.tapestry.IActionListener}
*  from the listener map (property <code>listeners</code>)
<li>A bean with a {@link org.apache.tapestry.spec.BeanLifecycle#PAGE}
*  lifecycle (property <code>beans</code>)
<li>A binding (property <code>bindings</code>)
</ul>
*
<p>
*  These optimizations have some inherent dangers; they assume that
*  the components have not overidden the specified properties;
*  the last one (concerning helper beans) assumes that the
*  component does inherit from {@link org.apache.tapestry.AbstractComponent}.
*  If this becomes a problem in the future, it may be necessary to
*  have the component itself involved in these determinations.
@author Howard Lewis Ship
@since 2.2
*
**/

public class ExpressionBinding extends AbstractBinding
{public static com.cortexeb.tools.clover.d __CLOVER_52_0 = com.cortexeb.tools.clover.aq.getRecorder(new char[] {67,58,92,119,111,114,107,115,112,97,99,101,92,106,97,107,97,114,116,97,45,116,97,112,101,115,116,114,121,92,102,114,97,109,101,119,111,114,107,92,116,97,114,103,101,116,92,99,108,111,118,101,114,45,100,98},1096998272901L);
    /**
     *  The root object against which the nested property name is evaluated.
     *
     **/

    private IComponent _root;

    /**
     *  The OGNL expression, as a string.
     *
     **/

    private String _expression;

    /**
     *  If true, then the binding is invariant, and cachedValue
     *  is the ultimate value.
     *
     **/

    private boolean _invariant = false;

    /**
     *  Stores the cached value for the binding, if invariant
     *  is true.
     *
     **/

    private Object _cachedValue;

    /**
     *   Parsed OGNL expression.
     *
     **/

    private Object _parsedExpression;

    /**
     *  Flag set once the binding has initialized.
     *  _cachedValue, _invariant and _final value
     *  for _expression
     *  are not valid until after initialization.
     *
     *
     **/

    private boolean _initialized;

    private ClassResolver _resolver;

    /**
     *  The OGNL context for this binding.  It is retained
     *  for the lifespan of the binding once created.
     *
     **/

    private Map _context;

    /**
     *  Creates a {@link ExpressionBinding} from the root object
     *  and an OGNL expression.
     *
     **/

    public ExpressionBinding(
        ClassResolver resolver,
        IComponent root,
        String expression,
        Location location)
    {
        super(location);__CLOVER_52_0.S[1200]++;try { __CLOVER_52_0.M[305]++;

        __CLOVER_52_0.S[1201]++;_resolver = resolver;
        __CLOVER_52_0.S[1202]++;_root = root;
        __CLOVER_52_0.S[1203]++;_expression = expression;
    } finally { }}

    public String getExpression()
    {try { __CLOVER_52_0.M[306]++;
        __CLOVER_52_0.S[1204]++;return _expression;
    } finally { }}

    public IComponent getRoot()
    {try { __CLOVER_52_0.M[307]++;
        __CLOVER_52_0.S[1205]++;return _root;
    } finally { }}

    /**
     *  Gets the value of the property path, with the assistance of a
     *  OGNL.
     *
     *  @throws BindingException if an exception is thrown accessing the property.
     *
     **/

    public Object getObject()
    {try { __CLOVER_52_0.M[308]++;
        __CLOVER_52_0.S[1206]++;initialize();

        __CLOVER_52_0.S[1207]++;if ((((_invariant) && (++__CLOVER_52_0.CT[235] != 0)) || (++__CLOVER_52_0.CF[235] == 0))){
            __CLOVER_52_0.S[1208]++;return _cachedValue;}

        __CLOVER_52_0.S[1209]++;return resolveProperty();
    } finally { }}

    private Object resolveProperty()
    {try { __CLOVER_52_0.M[309]++;
        __CLOVER_52_0.S[1210]++;try
        {
            __CLOVER_52_0.S[1211]++;return Ognl.getValue(_parsedExpression, getOgnlContext(), _root);
        }
        catch (Throwable t)
        {
            __CLOVER_52_0.S[1212]++;throw new BindingException(
                Tapestry.format(
                    "ExpressionBinding.unable-to-resolve-expression",
                    _expression,
                    _root),
                this,
                t);
        }
    } finally { }}

    /**
     *  Creates an OGNL context used to get or set a value.
     *  We may extend this in the future to set additional
     *  context variables (such as page, request cycle and engine).
     *  An optional type converter will be added to the OGNL context
     *  if it is specified as an application extension with the name
     *  {@link Tapestry#OGNL_TYPE_CONVERTER}.
     *
     **/

    private Map getOgnlContext()
    {try { __CLOVER_52_0.M[310]++;
        __CLOVER_52_0.S[1213]++;if ((((_context == null) && (++__CLOVER_52_0.CT[236] != 0)) || (++__CLOVER_52_0.CF[236] == 0))){
            __CLOVER_52_0.S[1214]++;_context = Ognl.createDefaultContext(_root, OgnlUtils.getOgnlClassResolver());}

        __CLOVER_52_0.S[1215]++;if ((((_root.getPage() != null) && (++__CLOVER_52_0.CT[237] != 0)) || (++__CLOVER_52_0.CF[237] == 0))){
        {
            __CLOVER_52_0.S[1216]++;if ((((_root.getPage().getEngine() != null) && (++__CLOVER_52_0.CT[238] != 0)) || (++__CLOVER_52_0.CF[238] == 0))){
            {
                __CLOVER_52_0.S[1217]++;IApplicationSpecification appSpec = _root.getPage().getEngine().getSpecification();

                __CLOVER_52_0.S[1218]++;if ((((appSpec != null && appSpec.checkExtension(Tapestry.OGNL_TYPE_CONVERTER)) && (++__CLOVER_52_0.CT[239] != 0)) || (++__CLOVER_52_0.CF[239] == 0))){
                {
                    __CLOVER_52_0.S[1219]++;TypeConverter typeConverter =
                        (TypeConverter) appSpec.getExtension(
                            Tapestry.OGNL_TYPE_CONVERTER,
                            TypeConverter.class);

                    __CLOVER_52_0.S[1220]++;Ognl.setTypeConverter(_context, typeConverter);
                }}
            }}
        }}

        __CLOVER_52_0.S[1221]++;return _context;
    } finally { }}

    /**
     *  Returns true if the binding is expected to always
     *  return the same value.
     *
     *
     **/

    public boolean isInvariant()
    {try { __CLOVER_52_0.M[311]++;
        __CLOVER_52_0.S[1222]++;initialize();

        __CLOVER_52_0.S[1223]++;return _invariant;
    } finally { }}

    public void setBoolean(boolean value)
    {try { __CLOVER_52_0.M[312]++;
        __CLOVER_52_0.S[1224]++;setObject((((value ) && (++__CLOVER_52_0.CT[240] != 0)) || (++__CLOVER_52_0.CF[240] == 0))? Boolean.TRUE : Boolean.FALSE);
    } finally { }}

    public void setInt(int value)
    {try { __CLOVER_52_0.M[313]++;
        __CLOVER_52_0.S[1225]++;setObject(new Integer(value));
    } finally { }}

    public void setDouble(double value)
    {try { __CLOVER_52_0.M[314]++;
        __CLOVER_52_0.S[1226]++;setObject(new Double(value));
    } finally { }}

    public void setString(String value)
    {try { __CLOVER_52_0.M[315]++;
        __CLOVER_52_0.S[1227]++;setObject(value);
    } finally { }}

    /**
     *  Sets up the helper object, but also optimizes the property path
     *  and determines if the binding is invarant.
     *
     **/

    private void initialize()
    {try { __CLOVER_52_0.M[316]++;
        __CLOVER_52_0.S[1228]++;if ((((_initialized) && (++__CLOVER_52_0.CT[241] != 0)) || (++__CLOVER_52_0.CF[241] == 0))){
            __CLOVER_52_0.S[1229]++;return;}

        __CLOVER_52_0.S[1230]++;_initialized = true;

        __CLOVER_52_0.S[1231]++;try
        {
            __CLOVER_52_0.S[1232]++;_parsedExpression = OgnlUtils.getParsedExpression(_expression);
        }
        catch (Exception ex)
        {
            __CLOVER_52_0.S[1233]++;throw new BindingException(ex.getMessage(), this, ex);
        }

        __CLOVER_52_0.S[1234]++;if ((((checkForConstant()) && (++__CLOVER_52_0.CT[242] != 0)) || (++__CLOVER_52_0.CF[242] == 0))){
            __CLOVER_52_0.S[1235]++;return;}

        __CLOVER_52_0.S[1236]++;try
        {
            __CLOVER_52_0.S[1237]++;if ((((!Ognl.isSimpleNavigationChain(_parsedExpression, getOgnlContext())) && (++__CLOVER_52_0.CT[243] != 0)) || (++__CLOVER_52_0.CF[243] == 0))){
                __CLOVER_52_0.S[1238]++;return;}
        }
        catch (OgnlException ex)
        {
            __CLOVER_52_0.S[1239]++;throw new BindingException(ex.getMessage(), this, ex);
        }

        // Split the expression into individual property names.
        // We then optimize what we can from the expression.  This will
        // shorten the expression and, in some cases, eliminate
        // it.  We also check to see if the binding can be an invariant.

        __CLOVER_52_0.S[1240]++;String[] split = new StringSplitter('.').splitToArray(_expression);

        __CLOVER_52_0.S[1241]++;int count = optimizeRootObject(split);

        // We'ver removed some or all of the initial elements of split
        // but have to account for anthing left over.

        __CLOVER_52_0.S[1242]++;if ((((count == split.length) && (++__CLOVER_52_0.CT[244] != 0)) || (++__CLOVER_52_0.CF[244] == 0))){
        {
            // The property path was something like "page" or "component.foo"
            // and was completely eliminated.

            __CLOVER_52_0.S[1243]++;_expression = null;
            __CLOVER_52_0.S[1244]++;_parsedExpression = null;

            __CLOVER_52_0.S[1245]++;_invariant = true;
            __CLOVER_52_0.S[1246]++;_cachedValue = _root;

            __CLOVER_52_0.S[1247]++;return;
        }}

        __CLOVER_52_0.S[1248]++;_expression = reassemble(count, split);
        __CLOVER_52_0.S[1249]++;_parsedExpression = OgnlUtils.getParsedExpression(_expression);

        __CLOVER_52_0.S[1250]++;checkForInvariant(count, split);
    } finally { }}

    /**
     *  Looks for common prefixes on the expression (provided pre-split) that
     *  are recognized as references to other components.
     *
     *  @return the number of leading elements of the split expression that
     *  have been removed.
     *
     **/

    private int optimizeRootObject(String[] split)
    {try { __CLOVER_52_0.M[317]++;
        __CLOVER_52_0.S[1251]++;int i;

        __CLOVER_52_0.S[1252]++;for (i = 0; (((i < split.length) && (++__CLOVER_52_0.CT[245] != 0)) || (++__CLOVER_52_0.CF[245] == 0)); i++){
        {

            __CLOVER_52_0.S[1253]++;if ((((split[i].equals("page")) && (++__CLOVER_52_0.CT[246] != 0)) || (++__CLOVER_52_0.CF[246] == 0))){
            {
                __CLOVER_52_0.S[1254]++;_root = _root.getPage();
                __CLOVER_52_0.S[1255]++;continue;
            }}

            __CLOVER_52_0.S[1256]++;if ((((split[i].equals("container")) && (++__CLOVER_52_0.CT[247] != 0)) || (++__CLOVER_52_0.CF[247] == 0))){
            {
                __CLOVER_52_0.S[1257]++;_root = _root.getContainer();
                __CLOVER_52_0.S[1258]++;continue;
            }}

            // Here's the tricky one ... if its of the form
            // "components.foo" we can get the named component
            // directly.

            __CLOVER_52_0.S[1259]++;if ((((split[i].equals("components") && i + 1 < split.length) && (++__CLOVER_52_0.CT[248] != 0)) || (++__CLOVER_52_0.CF[248] == 0))){
            {
                __CLOVER_52_0.S[1260]++;_root = _root.getComponent(split[i + 1]);
                __CLOVER_52_0.S[1261]++;i++;
                __CLOVER_52_0.S[1262]++;continue;
            }}

            // Not a recognized prefix, break the loop

            __CLOVER_52_0.S[1263]++;break;
        }}

        __CLOVER_52_0.S[1264]++;return i;
    } finally { }}

    private boolean checkForConstant()
    {try { __CLOVER_52_0.M[318]++;
        __CLOVER_52_0.S[1265]++;try
        {
            __CLOVER_52_0.S[1266]++;if ((((OgnlUtils.isConstant(_parsedExpression)) && (++__CLOVER_52_0.CT[249] != 0)) || (++__CLOVER_52_0.CF[249] == 0))){
            {
                __CLOVER_52_0.S[1267]++;_invariant = true;

                __CLOVER_52_0.S[1268]++;_cachedValue = resolveProperty();

                __CLOVER_52_0.S[1269]++;return true;
            }}
        }
        catch (Exception ex)
        {
            __CLOVER_52_0.S[1270]++;throw new BindingException(
                Tapestry.format(
                    "ExpressionBinding.unable-to-resolve-expression",
                    _expression,
                    _root),
                this,
                ex);
        }

        __CLOVER_52_0.S[1271]++;return false;
    } finally { }}

    /**
     *  Reassembles the remainder of the split property path
     *  from the start point.
     *
     **/

    private String reassemble(int start, String[] split)
    {try { __CLOVER_52_0.M[319]++;
        __CLOVER_52_0.S[1272]++;int count = split.length - start;

        __CLOVER_52_0.S[1273]++;if ((((count == 0) && (++__CLOVER_52_0.CT[250] != 0)) || (++__CLOVER_52_0.CF[250] == 0))){
            __CLOVER_52_0.S[1274]++;return null;}

        __CLOVER_52_0.S[1275]++;if ((((count == 1) && (++__CLOVER_52_0.CT[251] != 0)) || (++__CLOVER_52_0.CF[251] == 0))){
            __CLOVER_52_0.S[1276]++;return split[split.length - 1];}

        __CLOVER_52_0.S[1277]++;StringBuffer buffer = new StringBuffer();

        __CLOVER_52_0.S[1278]++;for (int i = start; (((i < split.length) && (++__CLOVER_52_0.CT[252] != 0)) || (++__CLOVER_52_0.CF[252] == 0)); i++){
        {
            __CLOVER_52_0.S[1279]++;if ((((i > start) && (++__CLOVER_52_0.CT[253] != 0)) || (++__CLOVER_52_0.CF[253] == 0))){
                __CLOVER_52_0.S[1280]++;buffer.append('.');}

            __CLOVER_52_0.S[1281]++;buffer.append(split[i]);
        }}

        __CLOVER_52_0.S[1282]++;return buffer.toString();
    } finally { }}

    /**
     *  Checks to see if the binding can be converted to an invariant.
     *
     **/

    private void checkForInvariant(int start, String[] split)
    {try { __CLOVER_52_0.M[320]++;
        // For now, all of our conditions are two properties
        // from a root component.

        __CLOVER_52_0.S[1283]++;if ((((split.length - start != 2) && (++__CLOVER_52_0.CT[254] != 0)) || (++__CLOVER_52_0.CF[254] == 0))){
            __CLOVER_52_0.S[1284]++;return;}

        __CLOVER_52_0.S[1285]++;try
        {
            __CLOVER_52_0.S[1286]++;if ((((!Ognl.isSimpleNavigationChain(_parsedExpression, getOgnlContext())) && (++__CLOVER_52_0.CT[255] != 0)) || (++__CLOVER_52_0.CF[255] == 0))){
                __CLOVER_52_0.S[1287]++;return;}
        }
        catch (OgnlException ex)
        {
            __CLOVER_52_0.S[1288]++;throw new BindingException(
                Tapestry.format(
                    "ExpressionBinding.unable-to-resolve-expression",
                    _expression,
                    _root),
                this,
                ex);
        }

        __CLOVER_52_0.S[1289]++;String first = split[start];

        __CLOVER_52_0.S[1290]++;if ((((first.equals("listeners")) && (++__CLOVER_52_0.CT[256] != 0)) || (++__CLOVER_52_0.CF[256] == 0))){
        {
            __CLOVER_52_0.S[1291]++;_invariant = true;

            // Could cast to AbstractComponent, get listenersMap, etc.,
            // but this is easier.

            __CLOVER_52_0.S[1292]++;_cachedValue = resolveProperty();
            __CLOVER_52_0.S[1293]++;return;
        }}

        __CLOVER_52_0.S[1294]++;if ((((first.equals("assets")) && (++__CLOVER_52_0.CT[257] != 0)) || (++__CLOVER_52_0.CF[257] == 0))){
        {
            __CLOVER_52_0.S[1295]++;String name = split[start + 1];

            __CLOVER_52_0.S[1296]++;_invariant = true;
            __CLOVER_52_0.S[1297]++;_cachedValue = _root.getAsset(name);
            __CLOVER_52_0.S[1298]++;return;
        }}

        __CLOVER_52_0.S[1299]++;if ((((first.equals("beans")) && (++__CLOVER_52_0.CT[258] != 0)) || (++__CLOVER_52_0.CF[258] == 0))){
        {
            __CLOVER_52_0.S[1300]++;String name = split[start + 1];

            __CLOVER_52_0.S[1301]++;IBeanSpecification bs = _root.getSpecification().getBeanSpecification(name);

            __CLOVER_52_0.S[1302]++;if ((((bs == null || bs.getLifecycle() != BeanLifecycle.PAGE) && (++__CLOVER_52_0.CT[259] != 0)) || (++__CLOVER_52_0.CF[259] == 0))){
                __CLOVER_52_0.S[1303]++;return;}

            // Again, could cast to AbstractComponent, but this
            // is easier.

            __CLOVER_52_0.S[1304]++;_invariant = true;
            __CLOVER_52_0.S[1305]++;_cachedValue = resolveProperty();
            __CLOVER_52_0.S[1306]++;return;
        }}

        __CLOVER_52_0.S[1307]++;if ((((first.equals("bindings")) && (++__CLOVER_52_0.CT[260] != 0)) || (++__CLOVER_52_0.CF[260] == 0))){
        {
            __CLOVER_52_0.S[1308]++;String name = split[start + 1];

            __CLOVER_52_0.S[1309]++;_invariant = true;
            __CLOVER_52_0.S[1310]++;_cachedValue = _root.getBinding(name);
            __CLOVER_52_0.S[1311]++;return;
        }}

        // Not a recognized pattern for conversion
        // to invariant.
    } finally { }}

    /**
     *  Updates the property for the binding to the given value. 
     *
     *  @throws BindingException if the property can't be updated (typically
     *  due to an security problem, or a missing mutator method).
     *  @throws ReadOnlyBindingException if the binding is invariant.
     **/

    public void setObject(Object value)
    {try { __CLOVER_52_0.M[321]++;
        __CLOVER_52_0.S[1312]++;initialize();

        __CLOVER_52_0.S[1313]++;if ((((_invariant) && (++__CLOVER_52_0.CT[261] != 0)) || (++__CLOVER_52_0.CF[261] == 0))){
            __CLOVER_52_0.S[1314]++;throw createReadOnlyBindingException(this);}

        __CLOVER_52_0.S[1315]++;try
        {
            __CLOVER_52_0.S[1316]++;Ognl.setValue(_parsedExpression, getOgnlContext(), _root, value);
        }
        catch (Throwable ex)
        {
            __CLOVER_52_0.S[1317]++;throw new BindingException(
                Tapestry.format(
                    "ExpressionBinding.unable-to-update-expression",
                    _expression,
                    _root,
                    value),
                this,
                ex);
        }
    } finally { }}

    /**
     *  Returns the a String representing the property path.  This includes
     *  the {@link IComponent#getExtendedId() extended id} of the root component
     *  and the property path ... once the binding is used, these may change
     *  due to optimization of the property path.
     *
     **/

    public String toString()
    {try { __CLOVER_52_0.M[322]++;
        __CLOVER_52_0.S[1318]++;StringBuffer buffer = new StringBuffer();

        __CLOVER_52_0.S[1319]++;buffer.append("ExpressionBinding[");
        __CLOVER_52_0.S[1320]++;buffer.append(_root.getExtendedId());

        __CLOVER_52_0.S[1321]++;if ((((_expression != null) && (++__CLOVER_52_0.CT[262] != 0)) || (++__CLOVER_52_0.CF[262] == 0))){
        {
            __CLOVER_52_0.S[1322]++;buffer.append(' ');
            __CLOVER_52_0.S[1323]++;buffer.append(_expression);
        }}

        __CLOVER_52_0.S[1324]++;if ((((_invariant) && (++__CLOVER_52_0.CT[263] != 0)) || (++__CLOVER_52_0.CF[263] == 0))){
        {
            __CLOVER_52_0.S[1325]++;buffer.append(" cachedValue=");
            __CLOVER_52_0.S[1326]++;buffer.append(_cachedValue);
        }}

        __CLOVER_52_0.S[1327]++;buffer.append(']');

        __CLOVER_52_0.S[1328]++;return buffer.toString();
    } finally { }}

}
TOP

Related Classes of org.apache.tapestry.binding.ExpressionBinding

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.