public ParameterConduit get()

in tapestry-core/src/main/java/org/apache/tapestry5/internal/transform/ParameterWorker.java [171:452]


            public ParameterConduit get(InstanceContext context)
            {
                final InternalComponentResources icr = context.get(InternalComponentResources.class);

                final Class fieldType = classCache.forName(fieldTypeName);

                final PerThreadValue<ParameterState> stateValue = perThreadManager.createValue();

                // Rely on some code generation in the component to set the default binding from
                // the field, or from a default method.

                return new ParameterConduit()
                {
                    // Default value for parameter, computed *once* at
                    // page load time.

                    private Object defaultValue = classCache.defaultValueForType(fieldTypeName);

                    private Binding parameterBinding;

                    boolean loaded = false;

                    private boolean invariant = false;

                    {
                        // Inform the ComponentResources about the parameter conduit, so it can be
                        // shared with mixins.

                        icr.setParameterConduit(parameterName, this);
                        icr.getPageLifecycleCallbackHub().addPageLoadedCallback(new Runnable()
                        {
                            public void run()
                            {
                                load();
                            }
                        });
                    }

                    private ParameterState getState()
                    {
                        ParameterState state = stateValue.get();

                        if (state == null)
                        {
                            state = new ParameterState();
                            state.value = defaultValue;
                            stateValue.set(state);
                        }

                        return state;
                    }

                    private boolean isLoaded()
                    {
                        return loaded;
                    }

                    public void set(Object instance, InstanceContext context, Object newValue)
                    {
                        ParameterState state = getState();

                        // Assignments before the page is loaded ultimately exist to set the
                        // default value for the field. Often this is from the (original)
                        // constructor method, which is converted to a real method as part of the transformation.

                        if (!loaded)
                        {
                            state.value = newValue;
                            defaultValue = newValue;
                            return;
                        }

                        // This will catch read-only or unbound parameters.

                        writeToBinding(newValue);

                        state.value = newValue;

                        // If caching is enabled for the parameter (the typical case) and the
                        // component is currently rendering, then the result
                        // can be cached in this ParameterConduit (until the component finishes
                        // rendering).

                        state.cached = annotation.cache() && icr.isRendering();
                    }

                    private Object readFromBinding()
                    {
                        Object result;

                        try
                        {
                            Object boundValue = parameterBinding.get();

                            result = typeCoercer.coerce(boundValue, fieldType);
                        } catch (RuntimeException ex)
                        {
                            throw new TapestryException(String.format(
                                    "Failure reading parameter '%s' of component %s: %s", parameterName,
                                    icr.getCompleteId(), ExceptionUtils.toMessage(ex)), parameterBinding, ex);
                        }

                        if (result == null && !allowNull)
                        {
                            throw new TapestryException(
                                    String.format(
                                            "Parameter '%s' of component %s is bound to null. This parameter is not allowed to be null.",
                                            parameterName, icr.getCompleteId()), parameterBinding, null);
                        }

                        return result;
                    }

                    private void writeToBinding(Object newValue)
                    {
                        // An unbound parameter acts like a simple field
                        // with no side effects.

                        if (parameterBinding == null)
                        {
                            return;
                        }

                        try
                        {
                            Object coerced = typeCoercer.coerce(newValue, parameterBinding.getBindingType());

                            parameterBinding.set(coerced);
                        } catch (RuntimeException ex)
                        {
                            throw new TapestryException(String.format(
                                    "Failure writing parameter '%s' of component %s: %s", parameterName,
                                    icr.getCompleteId(), ExceptionUtils.toMessage(ex)), icr, ex);
                        }
                    }

                    public void reset()
                    {
                        if (!invariant)
                        {
                            getState().reset(defaultValue);
                        }
                    }

                    public void load()
                    {
                        if (logger.isDebugEnabled())
                        {
                            logger.debug("{} loading parameter {}", icr.getCompleteId(), parameterName);
                        }

                        // If it's bound at this point, that's because of an explicit binding
                        // in the template or @Component annotation.

                        if (!icr.isBound(parameterName))
                        {
                            if (logger.isDebugEnabled())
                            {
                                logger.debug("{} parameter {} not yet bound", icr.getCompleteId(),
                                        parameterName);
                            }

                            // Otherwise, construct a default binding, or use one provided from
                            // the component.

                            Binding binding = getDefaultBindingForParameter();

                            if (logger.isDebugEnabled())
                            {
                                logger.debug("{} parameter {} bound to default {}", icr.getCompleteId(),
                                        parameterName, binding);
                            }

                            if (binding != null)
                            {
                                icr.bindParameter(parameterName, binding);
                            }
                        }

                        parameterBinding = icr.getBinding(parameterName);

                        loaded = true;

                        invariant = parameterBinding != null && parameterBinding.isInvariant();

                        getState().value = defaultValue;
                    }

                    public boolean isBound()
                    {
                        return parameterBinding != null;
                    }

                    public Object get(Object instance, InstanceContext context)
                    {
                        if (!isLoaded())
                        {
                            return defaultValue;
                        }

                        ParameterState state = getState();

                        if (state.cached || !isBound())
                        {
                            return state.value;
                        }

                        // Read the parameter's binding and cast it to the
                        // field's type.

                        Object result = readFromBinding();

                        // If the value is invariant, we can cache it until at least the end of the request (before
                        // 5.2, it would be cached forever in the pooled instance).
                        // Otherwise, we we may want to cache it for the remainder of the component render (if the
                        // component is currently rendering).

                        if (invariant || (annotation.cache() && icr.isRendering()))
                        {
                            state.value = result;
                            state.cached = true;
                        }

                        return result;
                    }

                    private Binding getDefaultBindingForParameter()
                    {
                        if (InternalUtils.isNonBlank(annotation.value()))
                        {
                            return bindingSource.newBinding("default " + parameterName, icr,
                                    annotation.defaultPrefix(), annotation.value());
                        }

                        if (annotation.autoconnect())
                        {
                            return defaultProvider.defaultBinding(parameterName, icr);
                        }

                        // Invoke the default method and install any value or Binding returned there.

                        invokeDefaultMethod();

                        return parameterBinding;
                    }

                    private void invokeDefaultMethod()
                    {
                        if (defaultMethodHandle == null)
                        {
                            return;
                        }

                        if (logger.isDebugEnabled())
                        {
                            logger.debug("{} invoking method {} to obtain default for parameter {}",
                                    icr.getCompleteId(), defaultMethodHandle, parameterName);
                        }

                        MethodInvocationResult result = defaultMethodHandle.invoke(icr.getComponent());

                        result.rethrow();

                        Object defaultValue = result.getReturnValue();

                        if (defaultValue == null)
                        {
                            return;
                        }

                        if (defaultValue instanceof Binding)
                        {
                            parameterBinding = (Binding) defaultValue;
                            return;
                        }

                        parameterBinding = new LiteralBinding(null, "default " + parameterName, defaultValue);
                    }


                };
            }