org.apache.wicket.AttributeModifier.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.AttributeModifier.java

Source

/*
 * 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.
 * <p>
 * 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.
 * <p>
 * If an attribute is not in the markup, this modifier will add an attribute.
 * <p>
 * Instances of this class should be added to components via the {@link Component#add(Behavior...)}
 * method after the component has been constructed.
 * <p>
 * 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:
 * 
 * <pre>
 * new AttributeModifier(&quot;myAttribute&quot;, model)
 * {
 *    protected String newValue(final String currentValue, final String replacementValue)
 *    {
 *       return currentValue + replacementValue;
 *    }
 * };
 * </pre>
 * 
 * @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));
    }
}