/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package javax.faces.component;
import java.io.
IOException;
import javax.el.
ValueExpression;
import javax.faces.
FactoryFinder;
import javax.faces.application.
FacesMessage;
import javax.faces.context.
FacesContext;
import javax.faces.convert.
Converter;
import javax.faces.convert.
ConverterException;
import javax.faces.render.
RenderKit;
import javax.faces.render.
RenderKitFactory;
import javax.faces.render.
Renderer;
/**
* <p class="changed_added_2_0"><strong class="changed_modified_2_2">UIViewParameter</strong> represents a
* binding between a request parameter and a model property or {@link UIViewRoot}
* property. This is a bi-directional binding.</p>
*
* <div class="changed_added_2_0">
* <p>The {@link javax.faces.view.ViewDeclarationLanguage}
* implementation must cause an instance of this component to appear in
* the view for each occurrence of an <code><f:viewParam /></code>
* element placed inside of an <code><f:metadata /></code>
* element. The user must place this facet within the
* <code>UIViewRoot</code>.</p>
* <p>Because this class extends <code>UIInput</code> any actions that
* one would normally take on a <code>UIInput</code> instance are valid
* for instances of this class. Instances of this class participate in
* the regular JSF lifecycle, including on Ajax requests.</p>
* </div>
*
* @since 2.0
*/
public class
UIViewParameter extends
UIInput {
// ------------------------------------------------------ Manifest Constants
/**
* <p>The standard component type for this component.</p>
*/
public static final
String COMPONENT_TYPE = "javax.faces.ViewParameter";
/**
* <p>The standard component family for this component.</p>
*/
public static final
String COMPONENT_FAMILY = "javax.faces.ViewParameter";
enum
PropertyKeys {
name,
submittedValue
}
// ------------------------------------------------------ Instance Variables
private
Renderer inputTextRenderer = null;
// ------------------------------------------------------------ Constructors
/**
* <p>Create a new {@link UIViewParameter} instance with default
* property values.</p>
*/
public
UIViewParameter() {
super();
setRendererType(null);
}
// ------------------------------------------------------ Instance Variables
/**
* <p>The raw value is the "implicit" binding for this view parameter. This property
* maintains the submitted value of the view parameter for the duration of the request.
* If the view parameter does not explicitly specify a value expression, then when the
* request ends, this value is stored with the state of this component to use as the
* submitted value on an ensuing postback.</p>
*/
private
String rawValue;
// -------------------------------------------------------------- Properties
@
Override
public
String getFamily() {
return (
COMPONENT_FAMILY);
}
/**
* <p class="changed_added_2_0">Return the request parameter name
* from which the value is retrieved.</p>
* @since 2.0
*/
public
String getName() {
return (
String)
getStateHelper().
eval(
PropertyKeys.
name);
}
/**
* <p class="changed_added_2_0">Set the request parameter name from
* which the value is retrieved.</p>
*
* @param name The new request parameter name.
* @since 2.0
*/
public void
setName(
String name) {
getStateHelper().
put(
PropertyKeys.
name,
name);
}
/**
* <p class="changed_added_2_0">Return <code>false</code>. The
* immediate setting is not relevant for view parameters and must be
* assumed to be <code>false</code>.</p>
* @since 2.0
*/
@
Override
public boolean
isImmediate() {
return false;
}
/**
* <p class="changed_added_2_0"><span class="changed_modified_2_2">Assume</span>
* that the submitted value is
* always a string, <span class="changed_added_2_2">but the return type
* from this method is <code>Object</code>.</span>.</p>
* @since 2.0
*/
@
Override
public
Object getSubmittedValue() {
return
getStateHelper().
get(
PropertyKeys.
submittedValue);
}
/**
* PENDING (docs) Interesting that submitted value isn't saved by the parent
* @param submittedValue The new submitted value
*/
@
Override
public void
setSubmittedValue(
Object submittedValue) {
getStateHelper().
put(
PropertyKeys.
submittedValue,
submittedValue);
}
// ----------------------------------------------------- UIComponent Methods
// This is the "Apply Request Phase" step
// QUESTION should we just override processDecodes() directly?
// ANSWER: In this case, no. We don't want to take responsibility for
// traversing any children we may have in the future.
/**
* <p class="changed_added_2_0">Override behavior from superclass to
* pull a value from the incoming request parameter map under the
* name given by {@link #getName} and store it with a call to {@link
* UIInput#setSubmittedValue}.</p>
* @since 2.0
*/
@
Override
public void
decode(
FacesContext context) {
if (
context == null) {
throw new
NullPointerException();
}
// QUESTION can we move forward and support an array? no different than UISelectMany; perhaps need to know
// if the value expression is single or multi-valued
// ANSWER: I'd rather not right now.
String paramValue =
context.
getExternalContext().
getRequestParameterMap().
get(
getName());
// submitted value will stay as previous value (null on initial request) if a parameter is absent
if (
paramValue != null) {
setSubmittedValue(
paramValue);
}
rawValue = (
String)
getSubmittedValue();
setValid(true);
}
/**
* <p class="changed_added_2_0">Specialize superclass behavior to treat
* <code>null</code> differently. In this class, a <code>null</code> value
* along with the "required" flag being set to <code>true</code> will
* cause a validation failure. </p>
* @param context
* @since 2.0
*/
@
Override
public void
processValidators(
FacesContext context) {
if (
context == null) {
throw new
NullPointerException();
}
// Skip processing if our rendered flag is false
if (!
isRendered()) {
return;
}
// we have to override since UIInput assumes that a null value means don't check
if (
getSubmittedValue() == null &&
isRequired()) {
String requiredMessageStr =
getRequiredMessage();
FacesMessage message;
if (null !=
requiredMessageStr) {
message = new
FacesMessage(
FacesMessage.
SEVERITY_ERROR,
requiredMessageStr,
requiredMessageStr);
} else {
message =
MessageFactory.
getMessage(
context,
REQUIRED_MESSAGE_ID,
MessageFactory.
getLabel(
context, this));
}
context.
addMessage(
getClientId(
context),
message);
setValid(false);
context.
validationFailed();
context.
renderResponse();
}
else {
super.processValidators(
context);
}
}
/**
* <p class="changed_added_2_0">Call through to superclass {@link
* UIInput#updateModel} then take the additional action of pushing
* the value into request scope if and only if the value is not a
* value expression, is valid, and the local value was set on this
* lifecycle execution.</p>
* @since 2.0
*/
@
Override
public void
updateModel(
FacesContext context) {
super.updateModel(
context);
if (!
hasValueExpression() &&
isValid() &&
isLocalValueSet()) {
// QUESTION should this be done even when a value expression is present?
// ANSWER: I don't see why not.
context.
getExternalContext().
getRequestMap().
put(
getName(),
getLocalValue());
}
}
// This is called during the real "Render Response" phase
/**
* <p class="changed_added_2_0">Called specially by {@link
* UIViewRoot#encodeEnd}, this method simply sets the submitted
* value to be the return from {@link #getStringValue}.</p>
* @since 2.0
*/
@
Override
public void
encodeAll(
FacesContext context) throws
IOException {
if (
context == null) {
throw new
NullPointerException();
}
// if there is a value expression, update view parameter w/ latest value after render
// QUESTION is it okay that a null string value may be suppressing the view parameter value?
// ANSWER: I'm not sure.
setSubmittedValue(
getStringValue(
context));
}
/**
* <p class="changed_added_2_0">If the value of this parameter comes
* from a <code>ValueExpression</code> return the value of the
* expression, otherwise, return the local value.</p>
*
* @since 2.0
*/
public
String getStringValue(
FacesContext context) {
String result = null;
if (
hasValueExpression()) {
result =
getStringValueFromModel(
context);
} else {
result = (null !=
rawValue) ?
rawValue : (
String)
getValue();
}
return
result;
}
/**
* <p class="changed_added_2_0">Manually perform standard conversion
* steps to get a string value from the value expression.</p>
*
* @since 2.0
*/
public
String getStringValueFromModel(
FacesContext context)
throws
ConverterException {
ValueExpression ve =
getValueExpression("value");
if (
ve == null) {
return null;
}
Object currentValue =
ve.
getValue(
context.
getELContext());
// If there is a converter attribute, use it to to ask application
// instance for a converter with this identifer.
Converter c =
getConverter();
if (
c == null) {
// if value is null and no converter attribute is specified, then
// return null (null has meaning for a view parameters; it means remove it).
if (
currentValue == null) {
return null;
}
// Do not look for "by-type" converters for Strings
if (
currentValue instanceof
String) {
return (
String)
currentValue;
}
// if converter attribute set, try to acquire a converter
// using its class type.
Class converterType =
currentValue.
getClass();
c =
context.
getApplication().
createConverter(
converterType);
// if there is no default converter available for this identifier,
// assume the model type to be String.
if (
c == null) {
return
currentValue.
toString();
}
}
return
c.
getAsString(
context, this,
currentValue);
}
/**
* <p class="changed_added_2_0">Because this class has no {@link
* Renderer}, leverage the one from the standard HTML_BASIC {@link
* RenderKit} with <code>component-family: javax.faces.Input</code>
* and <code>renderer-type: javax.faces.Text</code> and call its
* {@link Renderer#getConvertedValue} method.</p>
*
* @since 2.0
*/
@
Override
protected
Object getConvertedValue(
FacesContext context,
Object submittedValue)
throws
ConverterException {
return
getInputTextRenderer(
context).
getConvertedValue(
context, this,
submittedValue);
}
private
Renderer getInputTextRenderer(
FacesContext context) {
if (null ==
inputTextRenderer) {
RenderKitFactory rkf = (
RenderKitFactory)
FactoryFinder.
getFactory(
FactoryFinder.
RENDER_KIT_FACTORY);
RenderKit standard =
rkf.
getRenderKit(
context,
RenderKitFactory.
HTML_BASIC_RENDER_KIT);
inputTextRenderer =
standard.
getRenderer("javax.faces.Input", "javax.faces.Text");
}
assert(null !=
inputTextRenderer);
return
inputTextRenderer;
}
// ----------------------------------------------------- Helper Methods
private boolean
hasValueExpression() {
return null !=
getValueExpression("value");
}
/**
* <p class="changed_added_2_0">Inner class to encapsulate a
* <code>UIViewParameter</code> instance so that it may be safely
* referenced regardless of whether or not the current view is the
* same as the view in which this <code>UIViewParameter</code>
* resides.</p>
* @since 2.0
*/
public static class
Reference {
private
StateHolderSaver saver;
private int
indexInParent;
private
String viewIdAtTimeOfConstruction;
/**
* <p class="changed_added_2_0">Construct a reference to a
* <code>UIViewParameter</code>. This constructor cause the
* {@link StateHolder#saveState} method to be called on
* argument <code>UIViewParameter</code>.</p>
*
* @param context the <code>FacesContext</code>for this request
* @param indexInParent the index of the
* <code>UIViewParameter</code> in its parent
* <code>UIPanel</code>.
* @param viewIdAtTimeOfConstruction the viewId of the view in
* which the <code>UIViewParameter</code> is included. This may
* not be the same as the viewId from the <code>context</code>
* argument.
*
* @since 2.0
*/
public
Reference(
FacesContext context,
UIViewParameter param,
int
indexInParent,
String viewIdAtTimeOfConstruction) {
this.
saver = new
StateHolderSaver(
context,
param);
this.
indexInParent =
indexInParent;
this.
viewIdAtTimeOfConstruction =
viewIdAtTimeOfConstruction;
}
/**
* <p class="changed_added_2_0">Return the
* <code>UIViewParameter</code> to which this instance refers.
* If the current viewId is the same as the viewId passed to our
* constructor, use the index passed to the constructor to find
* the actual <code>UIViewParameter</code> instance and return
* it. Otherwise, call {@link StateHolder#restoreState} on the
* saved state and return the result.</p>
*
* @param context the <code>FacesContext</code>for this request
*
* @since 2.0
*/
public
UIViewParameter getUIViewParameter(
FacesContext context) {
UIViewParameter result = null;
UIViewRoot root =
context.
getViewRoot();
// If the view root is the same as when we were constructed...
if (this.
viewIdAtTimeOfConstruction.
equals(
root.
getViewId())) {
// get the actual view parameter from the tree...
UIComponent metadataFacet =
root.
getFacet(
UIViewRoot.
METADATA_FACET_NAME);
result = (
UIViewParameter)
metadataFacet.
getChildren().
get(
indexInParent);
} else {
// otherwise, use the saved one
result = (
UIViewParameter) this.
saver.
restore(
context);
}
return
result;
}
}
}