/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.myfaces.trinidadinternal.ui.laf.base.xhtml;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.apache.myfaces.trinidad.component.core.layout.CorePanelButtonBar;
import org.apache.myfaces.trinidad.context.RenderingContext;
import org.apache.myfaces.trinidad.skin.Skin;
import org.apache.myfaces.trinidad.util.IntegerUtils;
import org.apache.myfaces.trinidadinternal.agent.TrinidadAgent;
import org.apache.myfaces.trinidadinternal.share.url.FormEncoder;
import org.apache.myfaces.trinidadinternal.share.url.URLEncoder;
import org.apache.myfaces.trinidadinternal.ui.UIConstants;
import org.apache.myfaces.trinidadinternal.ui.AttributeKey;
import org.apache.myfaces.trinidadinternal.ui.MutableUINode;
import org.apache.myfaces.trinidadinternal.ui.NodeUtils;
import org.apache.myfaces.trinidadinternal.ui.UIXRenderingContext;
import org.apache.myfaces.trinidadinternal.ui.TextNode;
import org.apache.myfaces.trinidadinternal.ui.UINode;
import org.apache.myfaces.trinidadinternal.ui.beans.MarlinBean;
import org.apache.myfaces.trinidadinternal.ui.data.BoundValue;
import org.apache.myfaces.trinidadinternal.ui.data.DataObject;
import org.apache.myfaces.trinidadinternal.ui.data.DataObjectList;
import org.apache.myfaces.trinidadinternal.ui.data.bind.AccessKeyBoundValue;
import org.apache.myfaces.trinidadinternal.ui.laf.base.SkinTranslatedBoundValue;
import org.apache.myfaces.trinidad.context.PartialPageContext;
import org.apache.myfaces.trinidadinternal.ui.partial.PartialPageRendererUtils;
/**
* Renderer for Navigation Bars showing either single or multiple records.
*
* @version $Name: $ ($Revision: adfrt/faces/adf-faces-impl/src/main/java/oracle/adfinternal/view/faces/ui/laf/base/xhtml/NavigationBarRenderer.java#0 $) $Date: 10-nov-2005.18:54:04 $
* @deprecated This class comes from the old Java 1.2 UIX codebase and should not be used anymore.
*/
@Deprecated
public abstract class NavigationBarRenderer extends XhtmlLafRenderer
{
protected MutableUINode createSingleItemURLButton(
UIXRenderingContext context,
boolean isBack,
Object buttonText,
Object buttonAccessKey,
String destinationURL
)
{
MarlinBean urlButton = new MarlinBean(BUTTON_NAME);
urlButton.setAttributeValue(TEXT_ATTR, buttonText);
urlButton.setAttributeValue(ACCESS_KEY_ATTR, buttonAccessKey);
urlButton.setAttributeValue(DESTINATION_ATTR, destinationURL);
return urlButton;
}
protected MutableUINode createSingleItemSubmitButton(
UIXRenderingContext context,
boolean isBack,
Object buttonText,
Object buttonAccessKey,
String onClickJS
)
{
MarlinBean submitButton = new MarlinBean(BUTTON_NAME);
submitButton.setAttributeValue(TEXT_ATTR, buttonText);
submitButton.setAttributeValue(ACCESS_KEY_ATTR, buttonAccessKey);
submitButton.setOnClick(onClickJS);
return submitButton;
}
// =-=bwa: like the TableRenderer, this class creates anonymous children
// at render-time. This is a _temporary_ solution, until we
// decide how to create anonymous children for UINodes that
// can persist across renderings.
@Override
protected void prerender(
UIXRenderingContext context,
UINode node) throws IOException
{
URLEncoder encoder = context.getURLEncoder();
String eventKey = encoder.encodeParameter(EVENT_PARAM);
String sourceKey = encoder.encodeParameter(SOURCE_PARAM);
String valueKey = encoder.encodeParameter(VALUE_PARAM);
String sizeKey = encoder.encodeParameter(SIZE_PARAM);
String partialTargetsKey = encoder.encodeParameter(PARTIAL_TARGETS_PARAM);
// Get any partial targets - encoded in String form
String partialTargets = _getPartialTargets(context, node);
// determine real values through attributes
Number result =
(Number)node.getAttributeValue(context, BLOCK_SIZE_ATTR);
int visibleItemCount = (result != null)
? result.intValue()
: SINGLE_STEP;
if (visibleItemCount == SINGLE_STEP)
{
// we've got enough info, render as a single step
_renderSingleItemNavigator(context,
node,
eventKey,
sourceKey,
valueKey,
sizeKey,
partialTargetsKey,
partialTargets);
}
else
{
// render the multi-item navigator
_renderMultiItemNavigator(context,
node,
visibleItemCount,
eventKey,
sourceKey,
valueKey,
sizeKey,
partialTargetsKey,
partialTargets);
}
}
@Override
protected void postrender(
UIXRenderingContext context,
UINode node
) throws IOException
{
// do nothing
}
@Override
protected void renderContent(
UIXRenderingContext context,
UINode node
)
{
// Don't bother with renderContent - it's all done in the prerender
}
//
// Private methods
//
// render a single-step navigator
private void _renderSingleItemNavigator(
UIXRenderingContext context,
UINode navBar,
String eventKey,
String sourceKey,
String valueKey,
String sizeKey,
String partialTargetsKey,
String partialTargets
)
throws IOException
{
Number result;
// get current value
result = (Number)navBar.getAttributeValue(context, VALUE_ATTR);
long currentValue = (result != null)
? result.longValue()
: 1;
// get max value
long totalItems;
int childCount = navBar.getIndexedChildCount(context);
if (childCount > 0)
{
totalItems = childCount;
}
else
{
result = (Number)navBar.getAttributeValue(context, MAX_VALUE_ATTR);
totalItems = (result != null)
? result.longValue()
: MAX_VALUE_UNKNOWN;
}
boolean showBackButton = (currentValue > 1);
boolean showNextButton = ((totalItems == MAX_VALUE_UNKNOWN) ||
(currentValue < totalItems));
// bail if no buttons shown or values are bogus
if ((!showBackButton && !showNextButton) ||
((currentValue > totalItems) && (totalItems != MAX_VALUE_UNKNOWN)) ||
(currentValue < MAX_VALUE_UNKNOWN))
return;
// get form name (#1308799)
String formName = XhtmlLafUtils.getParentFormName(context);
// If we don't support navigation (e.g., printable pages),
// lie and claim we support scripting (even though we probably don't).
// This will give us the highest fidelity output - that is,
// we avoid creating submit buttons.
boolean supportsScripting = (supportsScripting(context) ||
!supportsNavigation(context));
if ((formName != null) && supportsScripting)
{
// render hidden fields to hold the form data
renderHiddenFields( context,
formName,
false,
eventKey,
sourceKey,
valueKey,
sizeKey,
partialTargetsKey,
partialTargets);
}
// get name
String nameString = _getNameString(context, navBar);
UINode backButton = null;
UINode nextButton = null;
// set up the back button
if (showBackButton)
{
BoundValue buttonTextandAccessKeyBV =
new SkinTranslatedBoundValue( _SINGLE_BACK_TEXT_KEY);
Object buttonTextBV = new AccessKeyBoundValue(buttonTextandAccessKeyBV,
false);
Object buttonAccessKeyBV = new AccessKeyBoundValue(
buttonTextandAccessKeyBV,
true);
// set the destination.
if (formName == null)
{
backButton = createSingleItemURLButton(
context,
true,
buttonTextBV,
buttonAccessKeyBV,
getSingleDestinationURL(context,
navBar,
eventKey,
sourceKey,
nameString,
valueKey,
currentValue-1));
}
else
{
if (supportsScripting)
{
backButton = createSingleItemSubmitButton(
context,
true,
buttonTextBV,
buttonAccessKeyBV,
getSingleDestinationSubmit( context,
navBar,
formName,
eventKey,
sourceKey,
nameString,
valueKey,
currentValue - 1,
false));
}
else
{
backButton = createSubmitButton(context,
buttonTextBV,
buttonAccessKeyBV,
null,
formName,
false,
eventKey,
sourceKey,
nameString,
valueKey,
currentValue - 1,
null,
-1);
}
}
}
// set up the next button
if (showNextButton)
{
String buttonTextKey = ((totalItems == 2)
? _SINGLE_CONTINUE_TEXT_KEY
: _SINGLE_NEXT_TEXT_KEY);
BoundValue buttonTextandAccessKeyBV =
new SkinTranslatedBoundValue(buttonTextKey);
Object buttonTextBV = new AccessKeyBoundValue( buttonTextandAccessKeyBV,
false);
Object buttonAccessKeyBV = new AccessKeyBoundValue(
buttonTextandAccessKeyBV,
true);
// The navBar needs its initial focus to be on the Next button,
// according to the BLAF. Render a special id on the Next button
// if this navBar is to have the initial focus. (unless it needs
// initial focus, the Next button does not have an id on it)
String buttonID = _getIDForFocus(context, navBar);
if (formName == null)
{
MutableUINode mutableNextButton =
createSingleItemURLButton(
context,
false,
buttonTextBV,
buttonAccessKeyBV,
getSingleDestinationURL(context,
navBar,
eventKey,
sourceKey,
nameString,
valueKey,
currentValue+1));
if (buttonID != null)
{
mutableNextButton.setID(buttonID);
}
nextButton = mutableNextButton;
}
else
{
// set the destination
if (supportsScripting)
{
MutableUINode mutableNextButton =
createSingleItemSubmitButton(
context,
false,
buttonTextBV,
buttonAccessKeyBV,
getSingleDestinationSubmit(
context,
navBar,
formName,
nameString,
currentValue + 1,
true));
if (buttonID != null)
{
mutableNextButton.setID(buttonID);
}
nextButton = mutableNextButton;
}
else
{
nextButton = createSubmitButton(context,
buttonTextBV,
buttonAccessKeyBV,
buttonID,
formName,
false,
eventKey,
sourceKey,
nameString,
valueKey,
currentValue + 1,
null,
-1);
}
}
}
// start the rendering
ResponseWriter writer = context.getResponseWriter();
boolean renderAsTable = _renderAsTable(context, navBar);
if (renderAsTable)
{
writer.startElement("table", navBar.getUIComponent());
renderLayoutTableAttributes(context, "0", null);
renderID(context, navBar);
writer.startElement("tr", null);
}
// we only want to render the ID in the "td" if renderAsTable is false.
// render the base ID the first time only, then we render the subIDs.
_renderStartTableCell(context, navBar, writer, renderAsTable, true);
// don't render back button on first step
if (showBackButton)
{
backButton.render(context);
writer.endElement("td");
_renderSpacerCell(context);
// we only want to render the ID in the "td" if renderAsTable is false.
// render the subID.
_renderStartTableCell(context, navBar, writer, renderAsTable, false);
}
//
// create the label and render it
//
writer.writeAttribute(NOWRAP_ATTRIBUTE, Boolean.TRUE, null);
// Now, we create one of two things. Normally, we'll create
// a simple range string. But, when there are link children,
// we create a ChoiceBean
if ((childCount == 0) ||
// No form when we require one means no choice
((formName == null) && requiresForm(context)) ||
// No scripting also means no choice
!supportsScripting(context))
{
// No "Step 1 of X" when there's 1 or two steps
if (totalItems > 2)
{
// the string to be displayed between buttons
String rangeString = _getRangeString(context,
navBar,
currentValue,
SINGLE_STEP,
totalItems,
null);
MarlinBean stepLabel = new MarlinBean(STYLED_TEXT_NAME);
stepLabel.setAttributeValue(TEXT_ATTR, rangeString);
stepLabel.setStyleClass(NAV_BAR_VIEW_STYLE_CLASS);
stepLabel.render(context);
}
}
else
{
// Gather the children
DataObjectList links = LinkDataObject.getLinkDataList(context,
navBar);
MarlinBean choice = new MarlinBean(CHOICE_NAME);
int maxVisited = (int) currentValue;
Object maxVisitedObj = navBar.getAttributeValue(context,
MAX_VISITED_ATTR);
if (maxVisitedObj instanceof Number)
maxVisited = ((Number) maxVisitedObj).intValue();
// Sanity check the "maxVisited" attribute
maxVisited = Math.min(maxVisited,
links == null ? 0 : links.getLength());
// Add one option per link, but only go up to the "maxVisited"
// value. (BTW, since maxVisited is 1-indexed, but i is 0-indexed,
// this is an _inclusive_ range here)
for (int i = 0; i < maxVisited; i++)
{
DataObject link = links.getItem(i);
MarlinBean option = new MarlinBean(OPTION_NAME);
option.setAttributeValue(TEXT_ATTR,
link.selectValue(context, TEXT_ATTR));
option.setAttributeValue(VALUE_ATTR, IntegerUtils.getString(i + 1));
if (currentValue == i + 1)
option.setAttributeValue(SELECTED_ATTR, Boolean.TRUE);
choice.addIndexedChild(option);
}
if (choice.getIndexedChildCount(context) > 0)
{
String onChange;
// get name
String name = _getNameString(context, navBar);
// Two options: formSubmitted mode and non-formSubmitted mode.
// First, non-formSubmitted mode.
if (formName == null)
{
onChange = _getChoiceOnChange(context,
_getDestinationString(context, navBar),
sourceKey,
eventKey,
name,
null,
null);
}
else
{
onChange = _getChoiceOnChangeFormSubmitted(context,
navBar,
formName,
eventKey,
sourceKey,
name,
partialTargetsKey,
null);
}
choice.setAttributeValue(ON_CHANGE_ATTR, onChange);
}
else
{
choice.setAttributeValue(READ_ONLY_ATTR, Boolean.TRUE);
}
choice.render(context);
}
// don't render the next button on last step
if (showNextButton)
{
writer.endElement("td");
_renderSpacerCell(context);
_renderStartTableCell(context, navBar, writer, renderAsTable, false);
nextButton.render(context);
}
writer.endElement("td");
if (renderAsTable)
{
writer.endElement("tr");
writer.endElement("table");
}
}
private String _getChoiceOnChange(
UIXRenderingContext context,
String destination,
String sourceKey,
String eventKey,
String name,
String partialTargets,
String partialTargetsKey)
{
int initialSize = destination.length() +
sourceKey.length() +
eventKey.length() +
name.length() +
8; // length of known keys/values +
// separators for each key/value
String[] keysAndValues;
String startScript;
String endScript;
if (partialTargets != null)
{
// Add in partialTargets parameter
keysAndValues = new String[] {
sourceKey, name, // ? ?
eventKey, GOTO_EVENT, // ? 4
partialTargetsKey, partialTargets
};
startScript = _FIRE_PARTIAL_CHANGE_START;
endScript = _FIRE_PARTIAL_CHANGE_END;
initialSize += (partialTargetsKey.length() +
partialTargets.length() +
2);
}
else
{
keysAndValues = new String[] {
sourceKey, name, // ? ?
eventKey, GOTO_EVENT // ? 4
};
startScript = _CHOICE_ON_CHANGE_START;
endScript = _CHOICE_ON_CHANGE_END;
}
int bufferlength = initialSize + startScript.length() + endScript.length();
StringBuffer buffer = new StringBuffer(bufferlength);
StringBuffer urlBuffer = new StringBuffer(initialSize);
buffer.append(startScript);
appendURLArguments(urlBuffer, destination, keysAndValues);
String url = urlBuffer.toString();
FacesContext facesContext = context.getFacesContext();
if(facesContext != null)
url = facesContext.getExternalContext().encodeActionURL(url);
buffer.append(url);
buffer.append(endScript);
return buffer.toString();
}
private String _getChoiceOnChangeFormSubmitted(
UIXRenderingContext context,
UINode node,
String form,
String eventKey,
String sourceKey,
String name,
String partialTargetsKey,
String partialTargets
)
{
// BUG 3557710 - FORM ENCODER AND POSTBACK HANDLING
FormEncoder formEncoder = context.getFormEncoder();
String encodedGotoEvent =
XhtmlLafUtils.getFormEncodedParameter(formEncoder, form, eventKey, GOTO_EVENT);
String encodedSource =
XhtmlLafUtils.getFormEncodedParameter(formEncoder, form, sourceKey, name);
String encodedPartialTargets =
XhtmlLafUtils.getFormEncodedParameter(formEncoder, form,
partialTargetsKey,
partialTargets);
int initialSize = _CHOICE_ON_CHANGE_FORM_START.length() +
form.length() +
13 +
encodedSource.length() +
_CHOICE_ON_CHANGE_FORM_END.length();
// Make room for partialTargets if we've got any
if (encodedPartialTargets != null)
initialSize += (encodedPartialTargets.length() + 2);
StringBuffer buffer = new StringBuffer(initialSize);
buffer.append(_CHOICE_ON_CHANGE_FORM_START);
buffer.append(form);
buffer.append("','");
buffer.append(encodedGotoEvent);
buffer.append("','");
buffer.append(encodedSource);
if (_doValidate(context, node))
buffer.append("',1");
else
buffer.append("',0");
if (encodedPartialTargets != null)
{
buffer.append(",null,");
buffer.append("'");
buffer.append(encodedPartialTargets);
buffer.append("'");
}
buffer.append(_CHOICE_ON_CHANGE_FORM_END);
return buffer.toString();
}
// render a multi-item navigator
private void _renderMultiItemNavigator(
UIXRenderingContext context,
UINode navBar,
int blockSize,
String eventKey,
String sourceKey,
String valueKey,
String sizeKey,
String partialTargetsKey,
String partialTargets
)
throws IOException
{
// get current value
Number result = (Number)navBar.getAttributeValue(context,
VALUE_ATTR);
long currentValue = (result != null)
? result.longValue()
: 1;
// get min value
result = (Number)navBar.getAttributeValue(context, MIN_VALUE_ATTR);
long minValue = (result != null)
? result.longValue()
: 1;
// get max value
result = (Number)navBar.getAttributeValue(context, MAX_VALUE_ATTR);
long maxValue = (result != null)
? result.longValue()
: MAX_VALUE_UNKNOWN;
// get destination
String destinationString = _getDestinationString(context, navBar);
// get name
String nameString = _getNameString(context, navBar);
// get form name (#1308799)
String formName = XhtmlLafUtils.getParentFormName(context);
int nextRecords = 0;
int backRecords = 0;
long backValue = 0;
long nextValue = 0;
if (blockSize > 0)
{
// determine how many records user can go forward
long lNextRecords = blockSize;
if (maxValue != MAX_VALUE_UNKNOWN)
{
// if we know the total records, align the current value to the
// start of its block. This makes the choice-rendering style
// not show extra back navigation records on the min block,
// which it would if not aligned.
/** no don't do this. see bug: 3052637
currentValue -= minValue;
currentValue /= blockSize;
currentValue *= blockSize;
currentValue += minValue;
*/
lNextRecords = maxValue - (currentValue + blockSize - 1);
}
// determine how many records user can go back
long lBackRecords = currentValue - minValue;
// trim
nextRecords = (lNextRecords > blockSize)
? blockSize
: (int) lNextRecords;
backRecords = (lBackRecords > blockSize)
? blockSize
: (int) lBackRecords;
backValue = currentValue - backRecords;
nextValue = currentValue + blockSize;
}
// calculate destinations
String prevDestination = null;
String nextDestination = null;
String prevOnClick = null;
String nextOnClick = null;
UINode leftArrow = null;
UINode rightArrow = null;
UINode leftLink = null;
UINode rightLink = null;
boolean validate = false;
boolean createSubmitButton = false;
boolean showDisabledNavigation = disabledNavigationShown(context);
boolean hasBackRecords = (backRecords > 0);
boolean hasNextRecords = (nextRecords > 0);
boolean showBackButton = hasBackRecords || showDisabledNavigation;
boolean showNextButton = hasNextRecords || showDisabledNavigation;
if (!supportsNavigation(context))
{
showBackButton = false;
showNextButton = false;
}
Object showAll = navBar.getAttributeValue(context, SHOW_ALL_ATTR);
boolean atShowAll = SHOW_ALL_ACTIVE.equals(showAll);
if (atShowAll)
{
backRecords = 0;
nextRecords = 0;
}
if (formName == null)
{
// use parameterized URLs
if (hasBackRecords && !atShowAll)
{
prevDestination = _getMultiDestinationURL(destinationString,
eventKey,
sourceKey,
nameString,
valueKey,
backValue,
sizeKey,
partialTargetsKey,
backRecords,
partialTargets);
if (partialTargets != null)
{
// If we're doing partial page rendering, we need to
// generate the _firePartialChange() onclick handler
prevOnClick = XhtmlLafUtils.getFirePartialChangeHandler(
prevDestination);
prevDestination = "#";
}
}
if (hasNextRecords && !atShowAll)
{
nextDestination = _getMultiDestinationURL(destinationString,
eventKey,
sourceKey,
nameString,
valueKey,
nextValue,
sizeKey,
partialTargetsKey,
nextRecords,
partialTargets);
if (partialTargets != null)
{
// If we're doing partial page rendering, we need to
// generate the _firePartialChange() onclick handler
nextOnClick = XhtmlLafUtils.getFirePartialChangeHandler(
nextDestination);
nextDestination = "#";
}
}
}
else
{
// determine whether we need to validate on submit
validate = _doValidate(context, navBar);
// use form submit
if (supportsScripting(context))
{
if (hasBackRecords && !atShowAll)
{
prevDestination = "#";
prevOnClick = _getMultiDestinationSubmit(context,
formName,
eventKey,
sourceKey,
nameString,
valueKey,
backValue,
sizeKey,
backRecords,
validate,
partialTargetsKey,
partialTargets);
}
if (hasNextRecords && !atShowAll)
{
nextDestination = "#";
nextOnClick = _getMultiDestinationSubmit(context,
formName,
eventKey,
sourceKey,
nameString,
valueKey,
nextValue,
sizeKey,
nextRecords,
validate,
partialTargetsKey,
partialTargets);
}
// render hidden fields to hold the form data
if (hasBackRecords || hasNextRecords)
renderHiddenFields( context,
formName,
true,
eventKey,
sourceKey,
valueKey,
sizeKey,
partialTargetsKey,
partialTargets);
}
else
{
// create submit buttons to handle submission
createSubmitButton = true;
}
}
//
// create the anonymous link nodes
//
if (createSubmitButton)
{
if (showBackButton)
{
// create back submit button bean
leftArrow = createSubmitButton(context,
_getMultiSubmitButtonText(context,
true,
backRecords),
null,
null,
formName,
validate,
eventKey,
sourceKey,
nameString,
valueKey,
backValue,
sizeKey,
backRecords);
}
// create next submit button bean
if (showNextButton)
{
rightArrow = createSubmitButton(context,
_getMultiSubmitButtonText(context,
false,
nextRecords),
null,
null,
formName,
validate,
eventKey,
sourceKey,
nameString,
valueKey,
nextValue,
sizeKey,
nextRecords);
}
}
else
{
if (showBackButton)
{
leftArrow = _createArrowImage(context,
true,
prevDestination,
prevOnClick);
leftLink = _createTextLink(context,
navBar,
true,
prevDestination,
prevOnClick,
backRecords);
}
if (showNextButton)
{
rightArrow = _createArrowImage(context,
false,
nextDestination,
nextOnClick);
rightLink = _createTextLink(context,
navBar,
false,
nextDestination,
nextOnClick,
nextRecords);
}
}
UINode rangeNode = _createRangeNode(context,
navBar,
destinationString,
nameString,
formName,
minValue,
currentValue,
blockSize,
maxValue,
eventKey,
sourceKey,
sizeKey,
partialTargetsKey,
partialTargets);
// ready to render
ResponseWriter writer = context.getResponseWriter();
boolean renderAsTable = _renderAsTable(context, navBar);
// The following strange code is part of the work around for
// bug 2275703. IE has problems re-laying out a TableBean
// after a partial page replacement. In particular, pieces
// of the table's top navigation bar, such as the previous link
// or icon, or sometimes the entire navigation bar, may shift to
// the left. In some cases, pieces of the navigation bar (the previous
// icon) may disappear during re-layout! There doesn't seem to be
// any clean way to avoid this apparent IE bug. However, we explicitly
// pareform a partial replacement of the navigation bar's previous icon
// *after* the entire table has been replaced, everything seems to lay
// out just fine.
//
// So, if we are rendering a TableBean's navigation bar with
// PPR enabled on IE, then we generate an ID for the nav bar's
// previous icon, and we add this to the list of rendered partial
// targets during the partial page render. This forces the icon
// to be replaced as part of the partial page update, and fixes
// our layout problems.
String iconID = null;
Object id = (supportsID(context) ? getID(context, navBar) : null);
TrinidadAgent agent = context.getAgent();
if ((id != null) &&
(partialTargets != null) &&
(agent.getAgentApplication() == TrinidadAgent.Application.IEXPLORER))
{
iconID = id.toString() + "-i";
}
// if we need to render standalone, create a table and table row...
if (renderAsTable)
{
writer.startElement("table", null);
renderLayoutTableAttributes(context, "0", null);
// We should always render the ID, but we particularly need
// to make sure that the ID is rendered if the NavBar is being
// used to navigate a TableBean, since we explicitly target
// TableBean NavBars when using PPR to re-render TableBeans...
renderID(context, navBar);
writer.startElement("tr", null);
}
// we only want to render the baseID, if needed, once. Then we
// render the subIDs. So we need to keep track of this.
boolean isBaseID = true;
if (leftArrow != null)
{
// We assign an id to the left arrow so that we can target it as
// a partial target to work around bug 2275703 - see note above.
if (iconID != null)
{
writer.startElement("td", null);
renderAttribute(context, "id", iconID);
// If the navigation bar that we are currently rendering
// is included in a partial page response, add the icon
// id to the list of partial targets.
PartialPageContext pprContext = context.getPartialPageContext();
if ((pprContext != null) &&
pprContext.isInsidePartialTarget())
{
pprContext.addRenderedPartialTarget(iconID);
}
}
else
{
// not in PPR mode, so just render the td (and id if not in a table
// for the Visual Editor)
_renderStartTableCell(context, navBar, writer, renderAsTable, isBaseID);
isBaseID = false;
}
writer.writeAttribute(VALIGN_ATTRIBUTE, "middle", null);
leftArrow.render(context);
writer.endElement("td");
_renderSpacerCell(context);
}
if (leftLink != null)
{
_renderStartTableCell(context, navBar, writer, renderAsTable, isBaseID);
isBaseID = false;
writer.writeAttribute(VALIGN_ATTRIBUTE, "middle", null);
writer.writeAttribute(NOWRAP_ATTRIBUTE, Boolean.TRUE, null);
leftLink.render(context);
writer.endElement("td");
_renderSpacerCell(context);
}
_renderStartTableCell(context, navBar, writer, renderAsTable, isBaseID);
isBaseID = false;
writer.writeAttribute(VALIGN_ATTRIBUTE, "middle", null);
writer.writeAttribute(NOWRAP_ATTRIBUTE, Boolean.TRUE, null);
rangeNode.render(context);
writer.endElement("td");
if (rightLink != null)
{
_renderSpacerCell(context);
_renderStartTableCell(context, navBar, writer, renderAsTable, false);
writer.writeAttribute(VALIGN_ATTRIBUTE, "middle", null);
writer.writeAttribute(NOWRAP_ATTRIBUTE, Boolean.TRUE, null);
rightLink.render(context);
writer.endElement("td");
}
if (rightArrow != null)
{
_renderSpacerCell(context);
_renderStartTableCell(context, navBar, writer, renderAsTable, false);
writer.writeAttribute(VALIGN_ATTRIBUTE, "middle", null);
rightArrow.render(context);
writer.endElement("td");
}
if (renderAsTable)
{
writer.endElement("tr");
writer.endElement("table");
}
}
/**
* Returns the destination, defaulting if none is provided
*/
private static String _getDestinationString(
UIXRenderingContext context,
UINode node
)
{
Object destination = node.getAttributeValue(context,
DESTINATION_ATTR);
String destinationString = null;
if (destination == null)
{
destinationString = context.getURLEncoder().getDefaultURL();
if (destinationString == null)
destinationString = "";
}
else
{
destinationString = destination.toString();
}
return destinationString;
}
/**
* Returns the name of the String, defaulting if none is provided
*/
private String _getNameString(
UIXRenderingContext context,
UINode node
)
{
Object name = node.getAttributeValue(context, NAME_ATTR);
if (name != null)
{
return name.toString();
}
else
{
return "";
}
}
// render a hidden field value, initialized to show that
// no navigation is taken place
// Note: This public method is called by TrainRenderer.java. The train
// uses the javascript submission code rendered here to do validation
public static void renderHiddenFields(
UIXRenderingContext context,
String formName,
boolean isMulti,
String eventKey,
String sourceKey,
String valueKey,
String sizeKey,
String partialTargetsKey,
String partialTargets
) throws IOException
{
assert (supportsScripting(context));
// first time we get a form-based navbar, render the
// necessary Javascript
if ((formName != null) &&
(context.getProperty(MARLIN_NAMESPACE,
_NAVBAR_SUBMIT_JAVASCRIPT_RENDERED) == null))
{
ResponseWriter writer = context.getResponseWriter();
// write the submit function
writer.startElement("script", null);
renderScriptDeferAttribute(context);
// Bug #3426092:
// render the type="text/javascript" attribute in accessibility mode
XhtmlLafRenderer.renderScriptTypeAttribute(context);
// write generic navbar submission code
writer.write(_getNavBarSubmitScript(eventKey,
sourceKey,
valueKey,
sizeKey,
partialTargetsKey));
// write choice submission code
writer.write(_CHOICE_SUBMIT_SCRIPT);
writer.endElement("script");
// push this property into the context so we only render once
context.setProperty(MARLIN_NAMESPACE,
_NAVBAR_SUBMIT_JAVASCRIPT_RENDERED,
Boolean.TRUE);
}
//only multi-step bars need the size field
FormValueRenderer.addNeededValue(context,
formName,
eventKey,
sourceKey,
valueKey,
(isMulti) ? sizeKey : null);
if (partialTargets != null)
{
URLEncoder encoder = context.getURLEncoder();
FormValueRenderer.addNeededValue(context,
formName,
partialTargetsKey,
encoder.encodeParameter(PARTIAL_PARAM),
null,
null);
}
}
// create the anonymous node displayed between the links
private UINode _createRangeNode(
UIXRenderingContext context,
UINode navBar,
String destination,
String name,
String form,
long minValue,
long currentValue,
int blockSize,
long maxValue,
String eventKey,
String sourceKey,
String sizeKey,
String partialTargetsKey,
String partialTargets
)
{
UINode rangeNode = null;
// if there is no blockSize to step by, or there are no items in the
// table, then we don't render a choice
if ((blockSize <= 0) ||
((maxValue < minValue) &&
(maxValue != MAX_VALUE_UNKNOWN)))
{
rangeNode = _getEmptyRangeNode();
}
else if (/*(maxValue == MAX_VALUE_UNKNOWN) ||*/ !supportsScripting(context))
{
// we don't know the size, so use a label
String rangeString = _getRangeString(context,
navBar,
currentValue,
blockSize,
maxValue,
null);
MarlinBean rangeLabel = new MarlinBean(STYLED_TEXT_NAME);
rangeLabel.setAttributeValue(TEXT_ATTR, rangeString);
rangeLabel.setAttributeValue(STYLE_CLASS_ATTR, NAV_BAR_VIEW_STYLE_CLASS);
rangeNode = rangeLabel;
}
else
{
// we know the size, so use the choice control
MarlinBean choice = new MarlinBean(CHOICE_NAME);
choice.setAttributeValue(SHORT_DESC_ATTR,
getTranslatedValue(context, _CHOICE_TIP_KEY));
String onChange = null;
int selectedIndex;
if (form == null)
{
// Bug #1765747: if we're trying to render a choice,
// Netscape will get very unhappy if there isn't a form,
// and render wacky output that pretty much trashes the
// navbar.
// If a client was _directly_ adding a choice, then this
// would be their fault, and we wouldn't try to work around
// it. But the NavigationBar implicitly adds the choice
// for them, and the Back/Next buttons are still useable
// even without the choice. So, the nice thing to do
// is just make the choice read-only.
if (requiresForm(context))
{
if (getParentFormName(context) == null)
{
// No form - turn on read-only
choice.setAttributeValue(READ_ONLY_ATTR, Boolean.TRUE);
}
}
// create each of the choice options for parameterized URLs
selectedIndex = _addNavigationOptions(context, navBar, choice, false,
minValue, maxValue, currentValue,
blockSize, sizeKey);
int count = choice.getIndexedChildCount(context);
if (count > 1)
{
onChange = _getChoiceOnChange(context,
destination,
sourceKey,
eventKey,
name,
partialTargets,
partialTargetsKey);
}
else
{
choice.setAttributeValue(READ_ONLY_ATTR, Boolean.TRUE);
}
}
else
{
// create each of the choice options for form submit
selectedIndex = _addNavigationOptions(context, navBar, choice, true,
minValue, maxValue, currentValue,
blockSize, sizeKey);
int count = choice.getIndexedChildCount(context);
if (count > 1)
{
onChange = _getChoiceOnChangeFormSubmitted(
context,
navBar,
form,
eventKey,
sourceKey,
name,
partialTargetsKey,
partialTargets);
}
else
{
choice.setAttributeValue(READ_ONLY_ATTR, Boolean.TRUE);
}
}
if (onChange != null)
{
// set the onchange handler
choice.setAttributeValue(ON_CHANGE_ATTR, onChange);
// set the onfocus handler to save the initial value
choice.setAttributeValue(ON_FOCUS_ATTR, _CHOICE_FORM_ON_FOCUS);
}
if (supportsID(context) &&
(selectedIndex >= 0) &&
!Boolean.TRUE.equals(choice.getAttributeValue(READ_ONLY_ATTR)))
{
String choiceId = XhtmlLafUtils.generateUniqueID(context);
choice.setID(choiceId);
StringBuffer text = new StringBuffer(26 + choiceId.length());
text.append("_setSelectIndexById(\"");
text.append(choiceId);
text.append("\",");
text.append(IntegerUtils.getString(selectedIndex));
text.append(")");
MarlinBean sb = new MarlinBean(SCRIPT_NAME);
sb.setAttributeValue(TEXT_ATTR, text.toString());
MarlinBean flb = new MarlinBean(FLOW_LAYOUT_NAME);
flb.addIndexedChild(choice);
flb.addIndexedChild(sb);
rangeNode = flb;
}
else
{
rangeNode = choice;
}
}
return rangeNode;
}
/**
* returns true if the navigation bar requires a form in order to
* submit
*/
protected boolean requiresForm(
UIXRenderingContext context
)
{
return true;
}
/**
* create each of the choice options and add them onto the choiceBean.
* @return the number of options added
*/
private int _addNavigationOptions(UIXRenderingContext context,
UINode navBar,
MarlinBean choice,
boolean isForm,
long minValue, long maxValue, long value,
int blockSize,
String sizeKey)
{
int selectedIndex = -1;
boolean maxUnknown = (maxValue == MAX_VALUE_UNKNOWN);
// Zero-indexed block index.
long blockIndex = (value - minValue + blockSize - 1L) /
blockSize;
// sometimes a record set won't start on a multiple of blockSize. So
// remember to add any offset:
// this can safely be an int because it is an index into the blockSize,
// which is itself an int:
int offset = (int) (value - (minValue + (blockIndex * blockSize)));
if (offset < 0)
offset = offset + blockSize;
// Total number of blocks (again, zero-indexed)
long maxBlockIndex;
if (maxUnknown)
maxBlockIndex = blockIndex + 1;
else
{
maxBlockIndex = (maxValue - minValue - offset) / blockSize;
if (offset > 0)
maxBlockIndex++;
}
// Calculate the first block that should be shown. The order goes:
// Group 0: 0-28 + More
// Group 1:Previous + 29-56 + More
// Group 2:Previous + 57-84 + More
// etc..
long firstBlockIndex;
// If everything is visible, or we're in the first group, start at zero.
if ((maxBlockIndex <= (_MAX_VISIBLE_OPTIONS - 1L)) ||
(blockIndex <= (_MAX_VISIBLE_OPTIONS - 2L)))
firstBlockIndex = 0;
else
firstBlockIndex = ((blockIndex - 1L) / (_MAX_VISIBLE_OPTIONS - 2L)) *
(_MAX_VISIBLE_OPTIONS - 2L);
// And we always show a total of 30 groups (or straight to the end)
long lastBlockIndex = firstBlockIndex + (_MAX_VISIBLE_OPTIONS - 1L);
if (lastBlockIndex > maxBlockIndex)
lastBlockIndex = maxBlockIndex;
Object showAll = navBar.getAttributeValue(context, SHOW_ALL_ATTR);
boolean atShowAll = SHOW_ALL_ACTIVE.equals(showAll);
// If they want "Show All" add an option for it - but ONLY allow
// this option when there are less than 30 groups (maxBlockIndex
// start as zero, hence "29"), and only allow it when there's
// more than 1 visible item!
if (!maxUnknown &&
(lastBlockIndex > firstBlockIndex) &&
(maxBlockIndex <= (_MAX_VISIBLE_OPTIONS - 1L)) &&
(atShowAll || SHOW_ALL_YES.equals(showAll)))
{
choice.addIndexedChild(0,
_createShowAllOption(context,
maxValue,
atShowAll));
}
else
{
atShowAll = false;
}
DataObject indexNames = (DataObject) navBar.getAttributeValue(context,
_INDEX_NAMES_ATTR);
for (blockIndex = firstBlockIndex;
blockIndex <= lastBlockIndex;
blockIndex++)
{
long blockStart = minValue + (blockIndex * blockSize);
// if there is an offset, then adjust accordingly. for example, if the
// offset is 7 (and the blockSize is 10), then the new blockStarts are:
// 1-7, 8-17, 18-27, etc ...
if (offset > 0)
blockStart += (offset - blockSize);
final int currentRecordSize;
// check to see if this is the very first record set in a table using an
// offset:
if (blockStart < minValue)
{
// treat this specially. this is the 1-7 case from the example above:
blockStart = minValue;
currentRecordSize = offset;
}
else
{
currentRecordSize = blockSize;
}
Object text;
// Need "Previous..."
if ((blockIndex == firstBlockIndex) &&
(blockIndex != 0))
{
text = getTranslatedValue(context, _PREVIOUS_TEXT_KEY);
}
// Need "More..." (on the last block, either 'cause
// the total number of blocks is unknown or we've shown enough blocks
else if ((blockIndex == lastBlockIndex) &&
(maxUnknown || (lastBlockIndex < maxBlockIndex)))
{
text = getTranslatedValue(context, _MORE_TEXT_KEY);
}
else
{
text = null;
}
MarlinBean currOption = _createNavigationOption(context,
navBar,
isForm,
blockStart,
currentRecordSize,
maxValue,
// Don't select
atShowAll ?
minValue - 1 : value,
sizeKey,
text,
indexNames);
choice.addIndexedChild(currOption);
if (Boolean.TRUE.equals(currOption.getAttributeValue(SELECTED_ATTR)))
selectedIndex = choice.getIndexedChildCount(context) - 1;
}
return selectedIndex;
}
private UINode _createShowAllOption(
UIXRenderingContext context,
long maxValue,
boolean atShowAll)
{
MarlinBean option = new MarlinBean(OPTION_NAME);
option.setAttributeValue(VALUE_ATTR, VALUE_SHOW_ALL);
String[] parameters = new String[]{IntegerUtils.getString(maxValue)};
String showAllText = formatString(context,
getTranslatedString(context,
_SHOW_ALL_KEY),
parameters);
option.setAttributeValue(TEXT_ATTR, showAllText);
if (atShowAll)
option.setAttributeValue(SELECTED_ATTR, Boolean.TRUE);
return option;
}
// create a choice option when max value is known
private MarlinBean _createNavigationOption(
UIXRenderingContext context,
UINode navBar,
boolean isForm,
long blockStart,
int blockSize,
long maxValue,
long currValue,
String sizeKey,
Object text,
DataObject indexNames
)
{
MarlinBean option = new MarlinBean(OPTION_NAME);
if (text == null)
text = _getRangeString(context,
navBar,
blockStart,
blockSize,
maxValue,
indexNames);
option.setAttributeValue(TEXT_ATTR, text);
int actualBlockSize;
if (maxValue == MAX_VALUE_UNKNOWN)
{
actualBlockSize = blockSize;
}
else
{
actualBlockSize = (int)(maxValue - blockStart + 1);
if (actualBlockSize > blockSize)
actualBlockSize = blockSize;
}
// add the value for javascript
option.setAttributeValue(VALUE_ATTR, _getMultiDestinationURLEnd(blockStart,
sizeKey,
actualBlockSize,
isForm));
// select the correct one
if ((currValue >= blockStart) &&
(currValue < (blockStart + blockSize)))
option.setAttributeValue(SELECTED_ATTR, Boolean.TRUE);
return option;
}
/**
* Returns true if disabled navigation items should be shown
*/
protected boolean disabledNavigationShown(
UIXRenderingContext context
)
{
return true;
}
/**
* Returns the correct text to use for a submit button on a
* multi item submit navbar
*/
private String _getMultiSubmitButtonText(
UIXRenderingContext context,
boolean isBack,
int size
)
{
//
// Create correct rtl string
//
boolean isRTL = isRightToLeft(context);
char arrowChar = (isBack ^ isRTL)
? '<'
: '>';
String blockText = getBlockString(context, isBack, size);
StringBuffer buttonText = new StringBuffer(blockText.length() + 2);
if (isBack)
{
buttonText.append(arrowChar);
buttonText.append(NBSP_CHAR);
buttonText.append(blockText);
}
else
{
buttonText.append(blockText);
buttonText.append(NBSP_CHAR);
buttonText.append(arrowChar);
}
return buttonText.toString();
}
/**
* Creates the submit button bean
*/
public static UINode createSubmitButton(
UIXRenderingContext context,
Object buttonText,
Object buttonAccessKey,
String buttonID,
String formName,
boolean validate,
String eventKey,
String sourceKey,
String source,
String valueKey,
long value,
String sizeKey,
int size
)
{
MarlinBean submitButton = new MarlinBean(SUBMIT_BUTTON_NAME);
submitButton.setID(buttonID);
submitButton.setAttributeValue(FORM_NAME_ATTR, formName);
submitButton.setAttributeValue(UNVALIDATED_ATTR, !validate);
submitButton.setAttributeValue(TEXT_ATTR, buttonText);
submitButton.setAttributeValue(ACCESS_KEY_ATTR, buttonAccessKey);
submitButton.setAttributeValue(NAME_VALUES_ATTR,
_createKeyValueArray(eventKey,
sourceKey,
source,
valueKey,
value,
sizeKey,
size,
null,
null));
return submitButton;
}
// create one of the text links for navigation
private UINode _createTextLink(
UIXRenderingContext context,
UINode navBar,
boolean isBack,
String destination,
String onClick,
int records
)
{
String text = getBlockString(context, isBack, records);
MutableUINode link;
if (records > 0)
{
link = new MarlinBean(LINK_NAME);
link.setAttributeValue(DESTINATION_ATTR, destination);
link.setAttributeValue(ON_CLICK_ATTR, onClick);
link.setAttributeValue(STYLE_CLASS_ATTR, NAV_BAR_ALINK_STYLE_CLASS);
}
else
{
link = new MarlinBean(STYLED_TEXT_NAME);
link.setAttributeValue(STYLE_CLASS_ATTR, NAV_BAR_ILINK_STYLE_CLASS);
}
link.setAttributeValue(TEXT_ATTR, text);
// The navBar needs its initial focus to be on the Next button,
// according to the BLAF. Render a special id on the Next button
// if this navBar is to have the initial focus. (unless it needs
// initial focus, the Next button does not have an id on it)
if (!isBack)
{
String linkID = _getIDForFocus(context, navBar);
if (linkID != null)
{
link.setID(linkID);
}
}
return link;
}
protected String getIconURI(
UIXRenderingContext context,
boolean isBack,
boolean isEnabled
)
{
// get the image location
String iconName;
if (isBack)
{
if (isEnabled)
{
iconName = AF_TABLE_NB_PREV_ICON_NAME;
}
else
{
iconName = AF_TABLE_NB_PREV_DISABLED_ICON_NAME;
}
}
else
{
if (isEnabled)
{
iconName = AF_TABLE_NB_NEXT_ICON_NAME;
}
else
{
iconName = AF_TABLE_NB_NEXT_DISABLED_ICON_NAME;
}
}
Skin skin = context.getSkin();
RenderingContext arc = RenderingContext.getCurrentInstance();
FacesContext fContext = context.getFacesContext();
String iconURI = (String)(skin.getIcon(
iconName).getImageURI(fContext, arc));
return iconURI;
}
// create the arrow image used in multi-item navigation
private UINode _createArrowImage(
UIXRenderingContext context,
boolean isBack,
String destination,
String onClick
)
{
// get the image location
boolean isEnabled = (destination != null);
String iconURI = getIconURI(context, isBack, isEnabled);
// If we don't have an icon to render, return null so that
// we won't render anything
if (iconURI == null)
return null;
String shortDesKey;
if (isBack)
{
if (isEnabled)
{
shortDesKey = _PREVIOUS_DESC_KEY;
}
else
{
shortDesKey = _DISABLED_PREVIOUS_DESC_KEY;
}
}
else
{
if (isEnabled)
{
shortDesKey = _NEXT_DESC_KEY;
}
else
{
shortDesKey = _DISABLED_NEXT_DESC_KEY;
}
}
MarlinBean arrow = new MarlinBean(IMAGE_NAME);
arrow.setAttributeValue(SOURCE_ATTR, iconURI);
arrow.setAttributeValue(SHORT_DESC_ATTR,
getTranslatedValue(context, shortDesKey));
// if not a link, we're done;
if (destination != null)
{
arrow.setAttributeValue(DESTINATION_ATTR, destination);
arrow.setOnClick(onClick);
}
return arrow;
}
/**
* Gets the string to use for next/previous links
* in a table navigation bar.
*/
protected String getBlockString(
UIXRenderingContext context,
boolean isBack,
int numRecords
)
{
// check to make sure that we have some records in this direction:
if (numRecords > 0)
{
String pattern = (isBack)
? getTranslatedString(context, _SELECT_RANGE_PREVIOUS_KEY)
: getTranslatedString(context, _SELECT_RANGE_NEXT_KEY);
String value = IntegerUtils.getString(numRecords);
String[] parameters = new String[]
{
value
};
return formatString(context, pattern, parameters);
}
else
{
// since we don't have any records, we are going to display some
// disabled text. see bug 1740486.
String text = (isBack)
? getTranslatedString(context, _SELECT_RANGE_DISABLED_PREVIOUS_KEY)
: getTranslatedString(context, _SELECT_RANGE_DISABLED_NEXT_KEY);
return text;
}
}
// get the string for the current range
private String _getRangeString(
UIXRenderingContext context,
UINode node,
long start,
int visibleItemCount,
long total,
DataObject indexNames
)
{
// formatter for generating the page string
String pattern;
String[] parameters;
if (visibleItemCount == SINGLE_STEP)
{
Object typeText = node.getAttributeValue(context, TYPE_TEXT_ATTR);
if (typeText == null)
typeText = getTranslatedString(context, _STEP_TEXT_KEY);
if (total == MAX_VALUE_UNKNOWN)
{
// =-= right now you can't ever get here because we don't show the
// step number if maxStep is unknown, but just in case...
pattern = getTranslatedString(context,
_SINGLE_RANGE_FORMAT_NO_TOTAL_STRING);
parameters = new String[]
{
typeText.toString(),
IntegerUtils.getString(start)
};
}
else
{
pattern = getTranslatedString(context, _SINGLE_RANGE_FORMAT_TOTAL_STRING);
parameters = new String[]
{
typeText.toString(),
IntegerUtils.getString(start),
IntegerUtils.getString(total)
};
}
}
else
{
// how many records do we really see now?
long currVisible = (total == MAX_VALUE_UNKNOWN)
? visibleItemCount
: total - start + 1;
if (currVisible > visibleItemCount)
currVisible = visibleItemCount;
String startParam;
String endParam;
if (indexNames != null)
{
Long startNum = Long.valueOf(start);
Long endNum = Long.valueOf(start + currVisible - 1);
Object o = indexNames.selectValue(context, startNum);
if (o != null)
startParam = o.toString();
else
startParam = startNum.toString();
o = indexNames.selectValue(context, endNum);
if (o != null)
endParam = o.toString();
else
endParam = endNum.toString();
pattern = getTranslatedString(context, _STRING_RANGE_FORMAT_STRING);
parameters = new String[]
{
startParam,
endParam
};
}
else
{
startParam = IntegerUtils.getString(start);
endParam = IntegerUtils.getString(start + currVisible - 1);
if (total == MAX_VALUE_UNKNOWN)
{
pattern = getTranslatedString(context,
_MULTI_RANGE_FORMAT_NO_TOTAL_STRING);
parameters = new String[]
{
startParam,
endParam
};
}
else
{
pattern = getTranslatedString(context,
_MULTI_RANGE_FORMAT_TOTAL_STRING);
parameters = new String[]
{
startParam,
endParam,
IntegerUtils.getString(total)
};
}
}
}
return formatString(context, pattern, parameters);
}
// generate a destination URL for single-step navigators
// Note: UINode is not guaranteed to be a navBar node.
// e.g., it could be a train node.
public static String getSingleDestinationURL(
UIXRenderingContext context,
UINode node,
String eventKey,
String sourceKey,
String name,
String valueKey,
long value
)
{
String baseURL = _getDestinationString(context, node);
String[] keysAndValues = {
eventKey, GOTO_EVENT,
sourceKey, name,
valueKey, IntegerUtils.getString(value)
};
return appendURLArguments(baseURL, keysAndValues);
}
// Note: UINode is not guaranteed to be a navBar node.
// e.g., it could be a train node.
public static String getSingleDestinationSubmit(
UIXRenderingContext context,
UINode node,
String form,
String name,
long value,
boolean doValidate
)
{
URLEncoder urlEncoder = context.getURLEncoder();
// this code might be refactored to avoid recomputing the parameter
// keys here, but the overhead is small anyway because the URLEncoder is
// typically a passthrough without renaming the parameters
String eventKey = urlEncoder.encodeParameter(UIConstants.EVENT_PARAM);
String sourceKey = urlEncoder.encodeParameter(UIConstants.SOURCE_PARAM);
String valueKey = urlEncoder.encodeParameter(UIConstants.VALUE_PARAM);
return getSingleDestinationSubmit(context, node, form, eventKey, sourceKey,
name, valueKey, value, doValidate);
}
// Note: UINode is not guaranteed to be a navBar node.
// e.g., it could be a train node.
public static String getSingleDestinationSubmit(
UIXRenderingContext context,
UINode node,
String form,
String eventKey,
String sourceKey,
String name,
String valueKey,
long value,
boolean doValidate
)
{
String valueString = IntegerUtils.getString(value);
// BUG 3557710 - FORM ENCODER AND POSTBACK HANDLING
FormEncoder formEncoder = context.getFormEncoder();
String encodedGotoEvent =
XhtmlLafUtils.getFormEncodedParameter(formEncoder, form, eventKey, GOTO_EVENT);
String encodedSource =
XhtmlLafUtils.getFormEncodedParameter(formEncoder, form,
sourceKey, name);
String encodedValue =
XhtmlLafUtils.getFormEncodedParameter(formEncoder, form,
valueKey, valueString);
if(form == null) form = "";
if(encodedSource == null) encodedSource = "";
boolean validate = false;
if(doValidate)
{
validate = _doValidate(context, node);
}
int bufferSize = _LINK_ON_CHANGE_FORM_START.length() +
form.length() +
25 + // appended literals below
encodedGotoEvent.length() +
encodedSource.length() +
encodedValue.length();
StringBuffer buffer = new StringBuffer(bufferSize);
buffer.append(_LINK_ON_CHANGE_FORM_START);
buffer.append(form);
buffer.append("','");
buffer.append(encodedGotoEvent);
buffer.append("','");
buffer.append(encodedSource);
if (validate)
buffer.append("',1,'");
else
buffer.append("',0,'");
buffer.append(encodedValue);
buffer.append("');return false");
return buffer.toString();
}
private String _getMultiDestinationSubmit(
UIXRenderingContext context,
String form,
String eventKey,
String sourceKey,
String name,
String valueKey,
long value,
String sizeKey,
int size,
boolean validate,
String partialTargetsKey,
String partialTargets
)
{
String valueString = IntegerUtils.getString(value);
String sizeString = IntegerUtils.getString(size);
// BUG 3557710 - FORM ENCODER AND POSTBACK HANDLING
FormEncoder formEncoder = context.getFormEncoder();
String encodedGotoEvent =
XhtmlLafUtils.getFormEncodedParameter(formEncoder, form,
eventKey, GOTO_EVENT);
String encodedSource =
XhtmlLafUtils.getFormEncodedParameter(formEncoder, form,
sourceKey, name);
String encodedValue =
XhtmlLafUtils.getFormEncodedParameter(formEncoder, form,
valueKey, valueString);
String encodedSize =
XhtmlLafUtils.getFormEncodedParameter(formEncoder, form,
sizeKey, sizeString);
String encodedPartialTargets =
XhtmlLafUtils.getFormEncodedParameter(formEncoder, form,
partialTargetsKey,
partialTargets);
int bufferSize = _LINK_ON_CHANGE_FORM_START.length() +
form.length() +
34 +
encodedGotoEvent.length() +
encodedSource.length() +
encodedValue.length() +
encodedSize.length();
if (partialTargets != null)
bufferSize += (partialTargets.length() + 2);
StringBuffer buffer = new StringBuffer(bufferSize);
buffer.append(_LINK_ON_CHANGE_FORM_START);
buffer.append(form);
buffer.append("', '");
buffer.append(encodedGotoEvent);
buffer.append("', '");
buffer.append(encodedSource);
if (validate)
buffer.append("',1,'");
else
buffer.append("',0,'");
buffer.append(encodedValue);
buffer.append("', '");
buffer.append(encodedSize);
buffer.append("',");
// Tack on the partial targets array
if (encodedPartialTargets != null)
{
buffer.append("'");
buffer.append(encodedPartialTargets);
buffer.append("'");
}
else
{
buffer.append("null");
}
buffer.append(");return false");
return buffer.toString();
}
private static String[] _createKeyValueArray(
String eventKey,
String sourceKey,
String source,
String valueKey,
long value,
String sizeKey,
int size,
String partialTargetsKey,
String partialTargets
)
{
int length = 8;
// Make room for partial targets if we've got some
if (partialTargets != null)
length += 2;
String[] keyValues = new String[length];
keyValues[0] = eventKey;
keyValues[1] = GOTO_EVENT;
keyValues[2] = sourceKey;
keyValues[3] = source;
keyValues[4] = valueKey;
keyValues[5] = IntegerUtils.getString(value);
keyValues[6] = sizeKey;
keyValues[7] = IntegerUtils.getString(size);
if (partialTargets != null)
{
keyValues[8] = partialTargetsKey;
keyValues[9] = partialTargets;
}
return keyValues;
}
/**
* Creates a submit button that will submit all of the name value pairs
* as a single
*/
private String _getMultiDestinationURL(
String baseURL,
String eventKey,
String sourceKey,
String source,
String valueKey,
long value,
String sizeKey,
String partialTargetsKey,
int size,
String partialTargets
)
{
return appendURLArguments(baseURL,
_createKeyValueArray(eventKey,
sourceKey,
source,
valueKey,
value,
sizeKey,
size,
partialTargetsKey,
partialTargets));
}
/**
* generate the end URL for onselects
*/
private String _getMultiDestinationURLEnd(
long value,
String sizeKey,
int size,
boolean isForm
)
{
String valueString = IntegerUtils.getString(value);
String sizeString = IntegerUtils.getString(size);
int bufferSize = valueString.length() +
sizeKey.length() +
2 +
sizeString.length();
StringBuffer buffer = new StringBuffer(bufferSize);
buffer.append(valueString);
if (isForm)
{
buffer.append(",");
}
else
{
buffer.append('&');
buffer.append(sizeKey);
buffer.append('=');
}
buffer.append(sizeString);
return buffer.toString();
}
private UINode _getEmptyRangeNode()
{
if (_sEmptyRangeNode == null)
{
_sEmptyRangeNode = new TextNode(NBSP_STRING);
}
return _sEmptyRangeNode;
}
private static String _getNavBarSubmitScript(
String eventKey,
String sourceKey,
String valueKey,
String sizeKey,
String partialTargetsKey
)
{
int len = _NAVBAR_SUBMIT_SCRIPT_LENGTH +
eventKey.length() +
sourceKey.length() +
valueKey.length() +
sizeKey.length();
StringBuffer buf = new StringBuffer(len);
buf.append(_NAVBAR_SUBMIT_SCRIPT[0]);
buf.append(eventKey);
buf.append(_NAVBAR_SUBMIT_SCRIPT[1]);
buf.append(sourceKey);
buf.append(_NAVBAR_SUBMIT_SCRIPT[2]);
buf.append(valueKey);
buf.append(_NAVBAR_SUBMIT_SCRIPT[3]);
buf.append(sizeKey);
buf.append(_NAVBAR_SUBMIT_SCRIPT[4]);
buf.append(partialTargetsKey);
buf.append(_NAVBAR_SUBMIT_SCRIPT[5]);
return buf.toString();
}
// don't render as a table in certain locations like a page button bar
private boolean _renderAsTable(
UIXRenderingContext context,
UINode navBar
)
{
UIComponent component = NodeUtils.getUIComponent(context, navBar);
if (component.getParent() instanceof CorePanelButtonBar)
return false;
return true;
}
// Utility method which tests whether the nav bar should validate
// prior to navigating
private static boolean _doValidate(
UIXRenderingContext context,
UINode navBar
)
{
return !Boolean.TRUE.equals(navBar.getAttributeValue(context,
UNVALIDATED_ATTR));
}
/**
* Writes the separator between two elements
*/
protected void renderItemSpacer(
UIXRenderingContext context
) throws IOException
{
char[] chars = new char[1];
chars[0] = NBSP_CHAR;
context.getResponseWriter().writeText(chars, 0, 1);
}
/**
* Writes the separator between two elements
*/
private void _renderSpacerCell(
UIXRenderingContext context
) throws IOException
{
ResponseWriter writer = context.getResponseWriter();
writer.startElement("td", null);
renderItemSpacer(context);
writer.endElement("td");
}
/*
* render the "td".
* we only want to render the ID in the "td" if renderAsTable is false.
* we only render the baseID if isBaseID is true. Otherwise we render the
* subID.
*/
private void _renderStartTableCell(
UIXRenderingContext context,
UINode node,
ResponseWriter writer,
boolean renderAsTable,
boolean isBaseID
) throws IOException
{
writer.startElement("td", null);
if (!renderAsTable)
{
if (isBaseID)
{
renderID(context, node);
}
}
}
// Gets the encoded partial targets for the specified node
private static String _getPartialTargets(
UIXRenderingContext context,
UINode node
)
{
if (!supportsPartialRendering(context))
return null;
String[] partialTargets = (String[])node.getAttributeValue(context,
PARTIAL_TARGETS_ATTR);
// Convert the partial targets to the encoded form
return PartialPageRendererUtils.encodePartialTargets(partialTargets);
}
private String _getIDForFocus(
UIXRenderingContext context,
UINode navBar
)
{
String id = null;
// The navBar needs its initial focus to be on the Next button,
// according to the BLAF. Render a special id on the Next button
// if this navBar is to have the initial focus. (unless it needs
// initial focus, the Next button does not have an id on it)
// We get body's initialFocus attribute off of the rendering context.
// If this is equal to the navBar's id, then we make up a new id
// for the Next button.
Object initialFocusID =
getRenderingProperty(context, INITIAL_FOCUS_CONTEXT_PROPERTY);
if ((initialFocusID != null))
{
Object navBarID = (supportsID(context) ? getID(context, navBar) : null);
if (initialFocusID.equals(navBarID))
{
// make up an id to use for the initial focus.
String focus = "-focus";
StringBuffer buffer = new StringBuffer(navBarID.toString().length()+
focus.length());
buffer.append(navBarID.toString());
buffer.append(focus);
id = buffer.toString();
// set the new id on the rendering context so that the body
// renderer can write it out to a script variable.
// A side-effect is that the initialFocusID in subsequent calls will
// never equal the navBar's id.
setRenderingProperty(context, INITIAL_FOCUS_CONTEXT_PROPERTY, id);
}
}
return id;
}
//
// Private variables
//
static private UINode _sEmptyRangeNode;
// resource keys
static private final String _SINGLE_BACK_TEXT_KEY =
"af_singleStepButtonBar.BACK";
static private final String _SINGLE_NEXT_TEXT_KEY =
"af_singleStepButtonBar.NEXT";
static private final String _SINGLE_CONTINUE_TEXT_KEY =
"af_singleStepButtonBar.CONTINUE";
static private final String _SINGLE_RANGE_FORMAT_TOTAL_STRING =
"af_singleStepButtonBar.FORMAT_TOTAL";
static private final String _SINGLE_RANGE_FORMAT_NO_TOTAL_STRING =
"af_singleStepButtonBar.FORMAT_NO_TOTAL";
static private final String _STEP_TEXT_KEY =
"af_singleStepButtonBar.STEP";
static private final String _PREVIOUS_DESC_KEY =
"af_table.SELECT_RANGE_PREVIOUS_TIP";
static private final String _NEXT_DESC_KEY =
"af_table.SELECT_RANGE_NEXT_TIP";
static private final String _DISABLED_PREVIOUS_DESC_KEY =
"af_table.SELECT_RANGE_PREV_DISABLED_TIP";
static private final String _DISABLED_NEXT_DESC_KEY =
"af_table.SELECT_RANGE_NEXT_DISABLED_TIP";
static private final String _MULTI_RANGE_FORMAT_TOTAL_STRING =
"af_table.SELECT_RANGE_CHOICE_FORMAT_TOTAL";
static private final String _MULTI_RANGE_FORMAT_NO_TOTAL_STRING =
"af_table.SELECT_RANGE_CHOICE_FORMAT_NO_TOTAL";
static private final String _CHOICE_TIP_KEY =
"af_table.SELECT_RANGE_CHOICE_TIP";
static private final String _SELECT_RANGE_PREVIOUS_KEY =
"af_table.SELECT_RANGE_PREVIOUS";
static private final String _SELECT_RANGE_NEXT_KEY =
"af_table.SELECT_RANGE_NEXT";
static private final String _SELECT_RANGE_DISABLED_PREVIOUS_KEY =
"af_table.SELECT_RANGE_DISABLED_PREVIOUS";
static private final String _SELECT_RANGE_DISABLED_NEXT_KEY =
"af_table.SELECT_RANGE_DISABLED_NEXT";
static private final String _SHOW_ALL_KEY =
"af_table.SELECT_RANGE_SHOW_ALL";
static private final String _PREVIOUS_TEXT_KEY =
"af_table.SELECT_RANGE_PREVIOUS_OPTION";
static private final String _MORE_TEXT_KEY =
"af_table.SELECT_RANGE_MORE_OPTION";
static private final String _STRING_RANGE_FORMAT_STRING =
"NAVBAR_STRING_RANGE_FORMAT";
static private final long _MAX_VISIBLE_OPTIONS = 30L;
static private final String _CHOICE_ON_CHANGE_START =
"window.self.location.href = '";
static private final String _CHOICE_ON_CHANGE_END =
"&value=' + this.options[this.selectedIndex].value";
static private final String _LINK_ON_CHANGE_FORM_START =
"_navBarSubmit('";
static private final String _CHOICE_ON_CHANGE_FORM_START =
"_navChoiceSubmit(this, '";
static private final String _CHOICE_ON_CHANGE_FORM_END =
")";
// navbar submission function that submits the form if the validation
// succeeds and returns whether the validation succeeded.
static private final String[] _NAVBAR_SUBMIT_SCRIPT =
{
"function _navBarSubmit(formName, event, navBar, vld, val, sze, partialTargets)"
+"{"
+ "var i = val.indexOf(',');"
+ "if (i >= 0)"
+ "{"
+ "sze = val.substring(i+1);"
+ "val = val.substring(0, i);"
+ "}"
+ "var submitFunc = (partialTargets == (void 0)) ? submitForm : "
+ "_submitPartialChange;"
+ "return submitFunc("
+ "formName,"
+ "vld,"
+ "{", // followed by the event name
":event,", // followed by the source
":navBar,", // followed by the value
":val,", // followed by the size
":sze,", // followed by partialTargets
":partialTargets});}"};
private static final int _NAVBAR_SUBMIT_SCRIPT_LENGTH =
XhtmlLafUtils.getLength(_NAVBAR_SUBMIT_SCRIPT);
// submits the form for the choice and rools back if validation fails
private static final String _CHOICE_SUBMIT_SCRIPT =
"function _navChoiceSubmit(choice, formName, event, navBar, vld, size, partialTargets)"
+"{"
+ "if (!_navBarSubmit(formName, event, navBar, vld, choice.options[choice.selectedIndex].value, size, partialTargets))"
+ "{"
+ "choice.selectedIndex = choice._lastValue;"
+ "}"
+"}";
// on focus handler for the form case. We save off the old value so that
// we can restore it if the validation chokes
private static final String _CHOICE_FORM_ON_FOCUS =
"this._lastValue = this.selectedIndex";
static private final String _NAVBAR_SUBMIT_JAVASCRIPT_RENDERED =
"_NAVBAR_SUBMIT_JAVASCRIPT_RENDERED";
// Partial page rendering scripts
private static final String _FIRE_PARTIAL_CHANGE_START =
"_firePartialChange(\'";
private static final String _FIRE_PARTIAL_CHANGE_END =
_CHOICE_ON_CHANGE_END + ");return false;";
// Private, undocumented attribute. We may expose this publicly,
// but for now, it's some OHW magic.
private static final AttributeKey _INDEX_NAMES_ATTR =
AttributeKey.getAttributeKey("indexNames");
}