Package org.apache.wicket.markup.parser

Source Code of org.apache.wicket.markup.parser.XmlTag

/*
* 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.markup.parser;

import java.util.Iterator;
import java.util.Map;

import org.apache.wicket.markup.MarkupElement;
import org.apache.wicket.util.lang.EnumeratedType;
import org.apache.wicket.util.lang.Objects;
import org.apache.wicket.util.string.AppendingStringBuffer;
import org.apache.wicket.util.string.StringValue;
import org.apache.wicket.util.string.Strings;
import org.apache.wicket.util.value.IValueMap;
import org.apache.wicket.util.value.ValueMap;


/**
* A subclass of MarkupElement which represents a tag including namespace and its optional
* attributes. XmlTags are returned by the XML parser.
*
* @author Jonathan Locke
*/
public class XmlTag extends MarkupElement
{
  /** A close tag, like </TAG>. */
  public static final Type CLOSE = new Type("CLOSE");

  /** An open tag, like <TAG componentId = "xyz">. */
  public static final Type OPEN = new Type("OPEN");

  /** An open/close tag, like <TAG componentId = "xyz"/>. */
  public static final Type OPEN_CLOSE = new Type("OPEN_CLOSE");

  /** Attribute map. */
  private IValueMap attributes;

  /** Column number. */
  int columnNumber;

  /** Length of this tag in characters. */
  int length;

  /** Line number. */
  int lineNumber;

  /** Name of tag, such as "img" or "input". */
  String name;

  /** Namespace of the tag, if available, such as <wicket:link ...> */
  String namespace;

  /** Position of this tag in the input that was parsed. */
  int pos;

  /** Full text of tag. */
  CharSequence text;

  /** The tag type (OPEN, CLOSE or OPEN_CLOSE). */
  Type type;

  /** Any component tag that this tag closes. */
  private XmlTag closes;

  /** If mutable, the immutable tag that this tag is a mutable copy of. */
  private XmlTag copyOf = this;

  /** True if this tag is mutable, false otherwise. */
  private boolean isMutable = true;

  /** True if the name of this tag was changed. */
  private boolean nameChanged = false;

  /**
   * Enumerated type for different kinds of component tags.
   */
  public static final class Type extends EnumeratedType
  {
    private static final long serialVersionUID = 1L;

    /**
     * Construct.
     *
     * @param name
     *            name of type
     */
    Type(final String name)
    {
      super(name);
    }
  }

  /**
   * Construct.
   */
  public XmlTag()
  {
    super();
  }

  /**
   * Gets whether this tag closes the provided open tag.
   *
   * @param open
   *            The open tag
   * @return True if this tag closes the given open tag
   */
  public final boolean closes(final XmlTag open)
  {
    return (closes == open) || (closes == open.copyOf);
  }

  /**
   * @see org.apache.wicket.markup.MarkupElement#equalTo(org.apache.wicket.markup.MarkupElement)
   */
  public final boolean equalTo(final MarkupElement element)
  {
    if (element instanceof XmlTag)
    {
      final XmlTag that = (XmlTag)element;
      if (!Objects.equal(getNamespace(), that.getNamespace()))
      {
        return false;
      }
      if (!getName().equals(that.getName()))
      {
        return false;
      }
      if (!getAttributes().equals(that.getAttributes()))
      {
        return false;
      }
      return true;
    }
    return false;
  }

  /**
   * Gets a hashmap of this tag's attributes.
   *
   * @return The tag's attributes
   */
  public IValueMap getAttributes()
  {
    if (attributes == null)
    {
      if ((copyOf == this) || (copyOf == null) || (copyOf.attributes == null))
      {
        attributes = new ValueMap();
      }
      else
      {
        attributes = new ValueMap(copyOf.attributes);
      }
    }
    return attributes;
  }

  /**
   * @return true if there 1 or more attributes.
   */
  public boolean hasAttributes()
  {
    return attributes != null && attributes.size() > 0;
  }

  /**
   * Get the column number.
   *
   * @return Returns the columnNumber.
   */
  public int getColumnNumber()
  {
    return columnNumber;
  }

  /**
   * Gets the length of the tag in characters.
   *
   * @return The tag's length
   */
  public int getLength()
  {
    return length;
  }

  /**
   * Get the line number.
   *
   * @return Returns the lineNumber.
   */
  public int getLineNumber()
  {
    return lineNumber;
  }

  /**
   * Gets the name of the tag, for example the tag <code>&lt;b&gt;</code>'s name would be 'b'.
   *
   * @return The tag's name
   */
  public String getName()
  {
    return name;
  }

  /**
   * Get whether the name of this component tag was changed.
   *
   * @return Returns true if the name of this component tag was changed
   */
  public boolean getNameChanged()
  {
    return nameChanged;
  }

  /**
   * Namespace of the tag, if available. For example, &lt;wicket:link&gt;.
   *
   * @return The tag's namespace
   */
  public String getNamespace()
  {
    return namespace;
  }

  /**
   * Assuming this is a close tag, return the corresponding open tag
   *
   * @return The open tag. Null, if no open tag available
   */
  public final XmlTag getOpenTag()
  {
    return closes;
  }

  /**
   * Gets the location of the tag in the input string.
   *
   * @return Tag location (index in input string)
   */
  public int getPos()
  {
    return pos;
  }

  /**
   * Get a string attribute.
   *
   * @param key
   *            The key
   * @return The string value
   */
  public CharSequence getString(final String key)
  {
    return getAttributes().getCharSequence(key);
  }

  /**
   * Get the tag type.
   *
   * @return the tag type (OPEN, CLOSE or OPEN_CLOSE).
   */
  public Type getType()
  {
    return type;
  }

  /**
   * Gets whether this is a close tag.
   *
   * @return True if this tag is a close tag
   */
  public boolean isClose()
  {
    return type == CLOSE;
  }

  /**
   *
   * @return True, if tag is mutable
   */
  public final boolean isMutable()
  {
    return isMutable;
  }

  /**
   * Gets whether this is an open tag.
   *
   * @return True if this tag is an open tag
   */
  public boolean isOpen()
  {
    return type == OPEN;
  }

  /**
   * Gets whether this tag is an open/ close tag.
   *
   * @return True if this tag is an open and a close tag
   */
  public boolean isOpenClose()
  {
    return type == OPEN_CLOSE;
  }

  /**
   * Compare tag name including namespace
   *
   * @param tag
   * @return true if name and namespace are equal
   */
  public boolean hasEqualTagName(final XmlTag tag)
  {
    if (!getName().equalsIgnoreCase(tag.getName()))
    {
      return false;
    }

    if ((getNamespace() == null) && (tag.getNamespace() == null))
    {
      return true;
    }

    if ((getNamespace() != null) && (tag.getNamespace() != null))
    {
      return getNamespace().equalsIgnoreCase(tag.getNamespace());
    }

    return false;
  }

  /**
   * Makes this tag object immutable by making the attribute map unmodifiable. Immutable tags
   * cannot be made mutable again. They can only be copied into new mutable tag objects.
   */
  public void makeImmutable()
  {
    if (isMutable)
    {
      isMutable = false;
      if (attributes != null)
      {
        attributes.makeImmutable();
        text = null;
      }
    }
  }

  /**
   * Gets this tag if it is already mutable, or a mutable copy of this tag if it is immutable.
   *
   * @return This tag if it is already mutable, or a mutable copy of this tag if it is immutable.
   */
  public XmlTag mutable()
  {
    if (isMutable)
    {
      return this;
    }
    else
    {
      final XmlTag tag = new XmlTag();
      copyPropertiesTo(tag);
      return tag;
    }
  }

  /**
   * Copies all internal properties from this tag to <code>dest</code>. This is basically
   * cloning without instance creation.
   *
   * @param dest
   *            tag whose properties will be set
   */
  void copyPropertiesTo(XmlTag dest)
  {
    dest.namespace = namespace;
    dest.name = name;
    dest.pos = pos;
    dest.length = length;
    dest.text = text;
    dest.type = type;
    dest.isMutable = true;
    dest.closes = closes;
    dest.copyOf = copyOf;
    if (attributes != null)
    {
      dest.attributes = new ValueMap(attributes);
    }
  }

  /**
   * Puts a boolean attribute.
   *
   * @param key
   *            The key
   * @param value
   *            The value
   * @return previous value associated with specified key, or null if there was no mapping for
   *         key. A null return can also indicate that the map previously associated null with the
   *         specified key, if the implementation supports null values.
   */
  public Object put(final String key, final boolean value)
  {
    return put(key, Boolean.toString(value));
  }

  /**
   * Puts an int attribute.
   *
   * @param key
   *            The key
   * @param value
   *            The value
   * @return previous value associated with specified key, or null if there was no mapping for
   *         key. A null return can also indicate that the map previously associated null with the
   *         specified key, if the implementation supports null values.
   */
  public Object put(final String key, final int value)
  {
    return put(key, Integer.toString(value));
  }

  /**
   * Puts a string attribute.
   *
   * @param key
   *            The key
   * @param value
   *            The value
   * @return previous value associated with specified key, or null if there was no mapping for
   *         key. A null return can also indicate that the map previously associated null with the
   *         specified key, if the implementation supports null values.
   */
  public Object put(final String key, final CharSequence value)
  {
    return getAttributes().put(key, value);
  }

  /**
   * Puts a {@link StringValue}attribute.
   *
   * @param key
   *            The key
   * @param value
   *            The value
   * @return previous value associated with specified key, or null if there was no mapping for
   *         key. A null return can also indicate that the map previously associated null with the
   *         specified key, if the implementation supports null values.
   */
  public Object put(final String key, final StringValue value)
  {
    return getAttributes().put(key, (value != null) ? value.toString() : null);
  }

  /**
   * Puts all attributes in map
   *
   * @param map
   *            A key/value map
   */
  public void putAll(final Map map)
  {
    for (final Iterator iterator = map.keySet().iterator(); iterator.hasNext();)
    {
      final String key = (String)iterator.next();
      Object value = map.get(key);
      put(key, (value != null) ? value.toString() : null);
    }
  }

  /**
   * Removes an attribute.
   *
   * @param key
   *            The key to remove
   */
  public void remove(final String key)
  {
    getAttributes().remove(key);
  }

  /**
   * Sets the tag name.
   *
   * @param name
   *            New tag name
   */
  public void setName(final String name)
  {
    if (isMutable)
    {
      this.name = name;
      this.nameChanged = true;
    }
    else
    {
      throw new UnsupportedOperationException("Attempt to set name of immutable tag");
    }
  }

  /**
   * Sets the tag namespace.
   *
   * @param namespace
   *            New tag name
   */
  public void setNamespace(final String namespace)
  {
    if (isMutable)
    {
      this.namespace = namespace;
      this.nameChanged = true;
    }
    else
    {
      throw new UnsupportedOperationException("Attempt to set namespace of immutable tag");
    }
  }

  /**
   * Assuming this is a close tag, assign it's corresponding open tag.
   *
   * @param tag
   *            the open-tag
   * @throws RuntimeException
   *             if 'this' is not a close tag
   */
  public void setOpenTag(final XmlTag tag)
  {
    this.closes = tag;
  }

  /**
   * Sets type of this tag if it is not immutable.
   *
   * @param type
   *            The new type
   */
  public void setType(final Type type)
  {
    if (isMutable)
    {
      this.type = type;
    }
    else
    {
      throw new UnsupportedOperationException("Attempt to set type of immutable tag");
    }
  }

  /**
   * Converts this object to a string representation.
   *
   * @return String version of this object
   */
  public String toDebugString()
  {
    return "[Tag name = " + name + ", pos = " + pos + ", line = " + lineNumber + ", length = " +
        length + ", attributes = [" + getAttributes() + "], type = " + type + "]";
  }

  /**
   * Converts this object to a string representation.
   *
   * @return String version of this object
   */
  public String toString()
  {
    return toCharSequence().toString();
  }

  /**
   * @see org.apache.wicket.markup.MarkupElement#toCharSequence()
   */
  public CharSequence toCharSequence()
  {
    if (!isMutable && (text != null))
    {
      return text;
    }

    return toXmlString(null);
  }

  /**
   * Converts this object to a string representation.
   *
   * @return String version of this object
   */
  public String toUserDebugString()
  {
    return "'" + toString() + "' (line " + lineNumber + ", column " + columnNumber + ")";
  }

  /**
   * Assuming some attributes have been changed, toXmlString() rebuilds the String on based on the
   * tags informations.
   *
   * @param attributeToBeIgnored
   * @return A xml string matching the tag
   */
  public CharSequence toXmlString(final String attributeToBeIgnored)
  {
    final AppendingStringBuffer buffer = new AppendingStringBuffer();

    buffer.append('<');

    if (type == CLOSE)
    {
      buffer.append('/');
    }

    if (namespace != null)
    {
      buffer.append(namespace);
      buffer.append(':');
    }

    buffer.append(name);

    final IValueMap attributes = getAttributes();
    if (attributes.size() > 0)
    {
      final Iterator iterator = attributes.keySet().iterator();
      for (; iterator.hasNext();)
      {
        final String key = (String)iterator.next();
        if ((key != null) &&
            ((attributeToBeIgnored == null) || !key
                .equalsIgnoreCase(attributeToBeIgnored)))
        {
          buffer.append(" ");
          buffer.append(key);
          CharSequence value = getString(key);

          // Attributes without values are possible, e.g. 'disabled'
          if (value != null)
          {
            buffer.append("=\"");
            value = Strings.replaceAll(value, "\"", "\\\"");
            buffer.append(value);
            buffer.append("\"");
          }
        }
      }
    }

    if (type == OPEN_CLOSE)
    {
      buffer.append('/');
    }

    buffer.append('>');

    return buffer;
  }
}
TOP

Related Classes of org.apache.wicket.markup.parser.XmlTag

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.