blob: 74ef146f7443c5e72464d014122a43b9cd364959 [file] [log] [blame]
// Copyright 2005 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;
import java.util.ArrayList;
import java.util.List;
import org.apache.hivemind.ApplicationRuntimeException;
import org.apache.hivemind.HiveMind;
import org.apache.hivemind.Location;
import org.apache.hivemind.util.Defense;
/**
* Constants and static methods.
*
* @author Howard M. Lewis Ship
* @since 4.0
*/
public class TapestryUtils
{
private static final char QUOTE = '\'';
private static final char BACKSLASH = '\\';
private static final String EMPTY_QUOTES = "''";
/**
* Stores an attribute into the request cycle, verifying that no object with that key is already
* present.
*
* @param cycle
* the cycle to store the attribute into
* @param key
* the key to store the attribute as
* @param object
* the attribute value to store
* @throws IllegalStateException
* if a non-null value has been stored into the cycle with the provided key.
*/
public static void storeUniqueAttribute(IRequestCycle cycle, String key, Object object)
{
Defense.notNull(cycle, "cycle");
Defense.notNull(key, "key");
Defense.notNull(object, "object");
Object existing = cycle.getAttribute(key);
if (existing != null)
throw new IllegalStateException(TapestryMessages.nonUniqueAttribute(
object,
key,
existing));
cycle.setAttribute(key, object);
}
public static final String PAGE_RENDER_SUPPORT_ATTRIBUTE = "org.apache.tapestry.PageRenderSupport";
public static final String FORM_ATTRIBUTE = "org.apache.tapestry.Form";
/**
* Stores the support object using {@link #storeUniqueAttribute(IRequestCycle, String, Object)}.
*/
public static void storePageRenderSupport(IRequestCycle cycle, PageRenderSupport support)
{
storeUniqueAttribute(cycle, PAGE_RENDER_SUPPORT_ATTRIBUTE, support);
}
/**
* Store the IForm instance using {@link #storeUniqueAttribute(IRequestCycle, String, Object)}.
*/
public static void storeForm(IRequestCycle cycle, IForm form)
{
storeUniqueAttribute(cycle, FORM_ATTRIBUTE, form);
}
/**
* Gets the previously stored {@link org.apache.tapestry.PageRenderSupport} object.
*
* @param cycle
* the request cycle storing the support object
* @param component
* the component which requires the support (used to report exceptions)
* @throws ApplicationRuntimeException
* if no support object has been stored
*/
public static PageRenderSupport getPageRenderSupport(IRequestCycle cycle, IComponent component)
{
Defense.notNull(component, "component");
PageRenderSupport result = getOptionalPageRenderSupport(cycle);
if (result == null)
throw new ApplicationRuntimeException(TapestryMessages.noPageRenderSupport(component),
component.getLocation(), null);
return result;
}
/**
* Gets the previously stored {@link IForm} object.
*
* @param cycle
* the request cycle storing the support object
* @param component
* the component which requires the form (used to report exceptions)
* @throws ApplicationRuntimeException
* if no form object has been stored
*/
public static IForm getForm(IRequestCycle cycle, IComponent component)
{
Defense.notNull(cycle, "cycle");
Defense.notNull(component, "component");
IForm result = (IForm) cycle.getAttribute(FORM_ATTRIBUTE);
if (result == null)
throw new ApplicationRuntimeException(TapestryMessages.noForm(component), component
.getLocation(), null);
return result;
}
public static void removePageRenderSupport(IRequestCycle cycle)
{
cycle.removeAttribute(PAGE_RENDER_SUPPORT_ATTRIBUTE);
}
public static void removeForm(IRequestCycle cycle)
{
cycle.removeAttribute(FORM_ATTRIBUTE);
}
/**
* Returns the {@link PageRenderSupport} object if previously stored, or null otherwise.
* This is used in the rare case that a component wishes to adjust its behavior based on whether
* the page render support services are avaiable (typically, adjust for whether enclosed by a
* Body component, or not).
*/
public static PageRenderSupport getOptionalPageRenderSupport(IRequestCycle cycle)
{
return (PageRenderSupport) cycle.getAttribute(PAGE_RENDER_SUPPORT_ATTRIBUTE);
}
/**
* Splits a string using the default delimiter of ','.
*/
public static String[] split(String input)
{
return split(input, ',');
}
/**
* Splits a single string into an array of strings, using a specific delimiter character.
*/
public static String[] split(String input, char delimiter)
{
if (HiveMind.isBlank(input))
return new String[0];
List strings = new ArrayList();
char[] buffer = input.toCharArray();
int start = 0;
int length = 0;
for (int i = 0; i < buffer.length; i++)
{
if (buffer[i] != delimiter)
{
length++;
continue;
}
// Consecutive delimiters will result in a sequence
// of empty strings.
String token = new String(buffer, start, length);
strings.add(token);
start = i + 1;
length = 0;
}
// If the string contains no delimiters, then
// wrap it an an array and return it.
if (start == 0 && length == buffer.length)
{
return new String[]
{ input };
}
// The final token.
String token = new String(buffer, start, length);
strings.add(token);
return (String[]) strings.toArray(new String[strings.size()]);
}
/**
* Enquotes a string within single quotes, ready for insertion as part of a block of JavaScript.
* Single quotes and backslashes within the input string are properly escaped.
*/
public static String enquote(String input)
{
if (input == null)
return EMPTY_QUOTES;
char[] chars = input.toCharArray();
// Add room for the two quotes and a couple of escaped characters
StringBuffer buffer = new StringBuffer(chars.length + 5);
buffer.append(QUOTE);
for (int i = 0; i < chars.length; i++)
{
char ch = chars[i];
if (ch == QUOTE || ch == BACKSLASH)
buffer.append(BACKSLASH);
buffer.append(ch);
}
buffer.append(QUOTE);
return buffer.toString();
}
/**
* A Tapestry component id is a little more liberal than an XML NMTOKEN. NMTOKEN must be
* [A-Za-z][A-Za-z0-9:_.-]*, but a component id might include a leading dollar sign (for an
* anonymous component with a fabricated id).
*/
public static String convertTapestryIdToNMToken(String baseId)
{
String result = baseId.replace('$', '_');
while (result.startsWith("_"))
result = result.substring(1);
return result;
}
/**
* Converts a clientId into a client-side DOM reference; i.e.
* <code>document.getElementById('<i>id</i>')</code>.
*/
public static String buildClientElementReference(String clientId)
{
Defense.notNull(clientId, "clientId");
return "document.getElementById('" + clientId + "')";
}
/**
* Used by some generated code; obtains a component and ensures it is of the correct type.
*/
public static IComponent getComponent(IComponent container, String componentId,
Class expectedType, Location location)
{
Defense.notNull(container, "container");
Defense.notNull(componentId, "componentId");
Defense.notNull(expectedType, "expectedType");
// Don't always have a location
IComponent component = null;
try
{
component = container.getComponent(componentId);
}
catch (Exception ex)
{
throw new ApplicationRuntimeException(ex.getMessage(), location, ex);
}
if (!expectedType.isAssignableFrom(component.getClass()))
throw new ApplicationRuntimeException(TapestryMessages.componentWrongType(
component,
expectedType), location, null);
return component;
}
}