org.apache.wicket.AttributeModifier Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wicket-core Show documentation
Show all versions of wicket-core Show documentation
Wicket is a Java web application framework that takes simplicity,
separation of concerns and ease of development to a whole new level.
Wicket pages can be mocked up, previewed and later revised using
standard WYSIWYG HTML design tools. Dynamic content processing and
form handling is all handled in Java code using a first-class
component model backed by POJO data beans that can easily be
persisted using your favorite technology.
The newest version!
/*
* 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.wicket;
import java.io.Serializable;
import org.apache.wicket.behavior.AttributeAppender;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.parser.XmlTag.TagType;
import org.apache.wicket.model.IComponentAssignedModel;
import org.apache.wicket.model.IDetachable;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.util.io.IClusterable;
import org.apache.wicket.util.lang.Args;
import org.apache.wicket.util.value.IValueMap;
/**
* This class allows a tag attribute of a component to be modified dynamically with a value obtained
* from a model object. This concept can be used to programmatically alter the attributes of
* components, overriding the values specified in the markup. The two primary uses of this class are
* to allow overriding of markup attributes based on business logic and to support dynamic
* localization. The replacement occurs as the component tag is rendered to the response.
*
* The attribute whose value is to be modified must be given on construction of the instance of this
* class along with the model containing the value to replace with.
*
* If an attribute is not in the markup, this modifier will add an attribute.
*
* Instances of this class should be added to components via the {@link Component#add(Behavior...)}
* method after the component has been constructed.
*
* It is possible to create new subclasses of {@code AttributeModifier} by overriding the
* {@link #newValue(String, String)} method. For example, you could create an
* {@code AttributeModifier} subclass which appends the replacement value like this:
*
*
* new AttributeModifier("myAttribute", model)
* {
* protected String newValue(final String currentValue, final String replacementValue)
* {
* return currentValue + replacementValue;
* }
* };
*
*
* @author Chris Turner
* @author Eelco Hillenius
* @author Jonathan Locke
* @author Martijn Dashorst
* @author Ralf Ebert
*/
public class AttributeModifier extends Behavior implements IClusterable
{
/**
* Special attribute value markers.
*/
public enum MarkerValue {
/** Marker value to have an attribute without a value added. */
VALUELESS_ATTRIBUTE_ADD,
/** Marker value to have an attribute without a value removed. */
VALUELESS_ATTRIBUTE_REMOVE
}
/** Marker value to have an attribute without a value added. */
public static final MarkerValue VALUELESS_ATTRIBUTE_ADD = MarkerValue.VALUELESS_ATTRIBUTE_ADD;
/** Marker value to have an attribute without a value removed. */
public static final MarkerValue VALUELESS_ATTRIBUTE_REMOVE = MarkerValue.VALUELESS_ATTRIBUTE_REMOVE;
private static final long serialVersionUID = 1L;
/** Attribute specification. */
private final String attribute;
/** The model that is to be used for the replacement. */
private final IModel> replaceModel;
/**
* Create a new attribute modifier with the given attribute name and model to replace with. The
* attribute will be added with the model value or the value will be replaced with the model
* value if the attribute is already present.
*
* @param attribute
* The attribute name to replace the value for
* @param replaceModel
* The model to replace the value with
*/
public AttributeModifier(final String attribute, final IModel> replaceModel)
{
Args.notNull(attribute, "attribute");
this.attribute = attribute;
this.replaceModel = replaceModel;
}
/**
* Create a new attribute modifier with the given attribute name and model to replace with. The
* attribute will be added with the model value or the value will be replaced with the value if
* the attribute is already present.
*
* @param attribute
* The attribute name to replace the value for
* @param value
* The value for the attribute
*/
public AttributeModifier(String attribute, Serializable value)
{
this(attribute, Model.of(value));
}
/**
* Detach the value if it was a {@link IDetachable}. Internal method, shouldn't be called from
* the outside. If the attribute modifier is shared, the detach method will be called multiple
* times.
*
* @param component
* the model that initiates the detachment
*/
@Override
public final void detach(Component component)
{
if (replaceModel != null)
replaceModel.detach();
}
/**
* @return the attribute name to replace the value for
*/
public final String getAttribute()
{
return attribute;
}
@Override
public final void onComponentTag(Component component, ComponentTag tag)
{
if (tag.getType() != TagType.CLOSE)
replaceAttributeValue(component, tag);
}
/**
* Checks the given component tag for an instance of the attribute to modify and if all criteria
* are met then replace the value of this attribute with the value of the contained model
* object.
*
* @param component
* The component
* @param tag
* The tag to replace the attribute value for
*/
public final void replaceAttributeValue(final Component component, final ComponentTag tag)
{
if (isEnabled(component))
{
final IValueMap attributes = tag.getAttributes();
final Object replacementValue = getReplacementOrNull(component);
if (VALUELESS_ATTRIBUTE_ADD == replacementValue)
{
attributes.put(attribute, null);
}
else if (VALUELESS_ATTRIBUTE_REMOVE == replacementValue)
{
attributes.remove(attribute);
}
else
{
final String value = toStringOrNull(attributes.get(attribute));
final Serializable newValue = newValue(value, toStringOrNull(replacementValue));
if (newValue == VALUELESS_ATTRIBUTE_REMOVE)
{
attributes.remove(attribute);
}
else if (newValue != null)
{
attributes.put(attribute, newValue);
}
}
}
}
@Override
public String toString()
{
return "[AttributeModifier attribute=" + attribute + ", replaceModel=" + replaceModel + "]";
}
/**
* gets replacement with null check.
*
* @param component
* @return replacement value
*/
private Object getReplacementOrNull(final Component component)
{
IModel> model = replaceModel;
if (model instanceof IComponentAssignedModel)
{
model = ((IComponentAssignedModel>)model).wrapOnAssignment(component);
}
return (model != null) ? model.getObject() : null;
}
/**
* gets replacement as a string with null check.
*
* @param replacementValue
* @return replacement value as a string
*/
private String toStringOrNull(final Object replacementValue)
{
return (replacementValue != null) ? replacementValue.toString() : null;
}
/**
* Gets the replacement model. Allows subclasses access to replace model.
*
* @return the replace model of this attribute modifier
*/
protected final IModel> getReplaceModel()
{
return replaceModel;
}
/**
* Gets the value that should replace the current attribute value. This gives users the ultimate
* means to customize what will be used as the attribute value. For instance, you might decide
* to append the replacement value to the current instead of just replacing it as is Wicket's
* default.
*
* @param currentValue
* The current attribute value. This value might be null!
* @param replacementValue
* The replacement value. This value might be null!
* @return The value that should replace the current attribute value
*/
protected Serializable newValue(final String currentValue, final String replacementValue)
{
return replacementValue;
}
/**
* Creates a attribute modifier that replaces the current value with the given value.
*
* @param attributeName
* @param value
* @return the attribute modifier
* @since 1.5
*/
public static AttributeModifier replace(String attributeName, IModel> value)
{
Args.notEmpty(attributeName, "attributeName");
return new AttributeModifier(attributeName, value);
}
/**
* Creates a attribute modifier that replaces the current value with the given value.
*
* @param attributeName
* @param value
* @return the attribute modifier
* @since 1.5
*/
public static AttributeModifier replace(String attributeName, Serializable value)
{
Args.notEmpty(attributeName, "attributeName");
return new AttributeModifier(attributeName, value);
}
/**
* Creates a attribute modifier that appends the current value with the given {@code value}
* using a default space character (' ') separator.
*
* @param attributeName
* @param value
* @return the attribute modifier
* @since 1.5
* @see AttributeAppender
*/
public static AttributeAppender append(String attributeName, IModel> value)
{
Args.notEmpty(attributeName, "attributeName");
return new AttributeAppender(attributeName, value).setSeparator(" ");
}
/**
* Creates a attribute modifier that appends the current value with the given {@code value}
* using a default space character (' ') separator.
*
* @param attributeName
* @param value
* @return the attribute modifier
* @since 1.5
* @see AttributeAppender
*/
public static AttributeAppender append(String attributeName, Serializable value)
{
Args.notEmpty(attributeName, "attributeName");
return append(attributeName, Model.of(value));
}
/**
* Creates a attribute modifier that prepends the current value with the given {@code value}
* using a default space character (' ') separator.
*
* @param attributeName
* @param value
* @return the attribute modifier
* @since 1.5
* @see AttributeAppender
*/
public static AttributeAppender prepend(String attributeName, IModel> value)
{
Args.notEmpty(attributeName, "attributeName");
return new AttributeAppender(attributeName, value)
{
private static final long serialVersionUID = 1L;
@Override
protected Serializable newValue(String currentValue, String replacementValue)
{
// swap currentValue and replacementValue in the call to the concatenator
return super.newValue(replacementValue, currentValue);
}
}.setSeparator(" ");
}
/**
* Creates a attribute modifier that prepends the current value with the given {@code value}
* using a default space character (' ') separator.
*
* @param attributeName
* @param value
* @return the attribute modifier
* @since 1.5
* @see AttributeAppender
*/
public static AttributeAppender prepend(String attributeName, Serializable value)
{
Args.notEmpty(attributeName, "attributeName");
return prepend(attributeName, Model.of(value));
}
/**
* Creates a attribute modifier that removes an attribute with the specified name
*
* @param attributeName
* the name of the attribute to be removed
* @return the attribute modifier
* @since 1.5
*/
public static AttributeModifier remove(String attributeName)
{
Args.notEmpty(attributeName, "attributeName");
return replace(attributeName, Model.of(VALUELESS_ATTRIBUTE_REMOVE));
}
}