Package org.apache.wicket.markup.parser

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

/*
* 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.parser.IXmlPullParser.HttpTagType;
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
{
  /**
   * Enumerated type for different kinds of component tags.
   */
  public static enum TagType {
    /** A close tag, like </TAG>. */
    CLOSE("CLOSE"),

    /** An open tag, like <TAG componentId = "xyz">. */
    OPEN("OPEN"),

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

    private String name;

    TagType(final String name)
    {
      this.name = name;
    }
  }

  TextSegment text;

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

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

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

  /** The tag type (OPEN, CLOSE or OPEN_CLOSE). */
  TagType 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;

  private HttpTagType httpTagType;

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

  /**
   * Construct.
   *
   * @param text
   * @param type
   */
  public XmlTag(final TextSegment text, final TagType type)
  {
    this.text = text;
    this.type = type;
  }

  /**
   * 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) && (this != open));
  }

  /**
   * @param element
   * @return true, if namespace, name and attributes are the same
   */
  public final boolean equalTo(final XmlTag element)
  {
    final XmlTag that = element;
    if (!Objects.equal(getNamespace(), that.getNamespace()))
    {
      return false;
    }
    if (!getName().equals(that.getName()))
    {
      return false;
    }
    return getAttributes().equals(that.getAttributes());
  }

  /**
   * 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 (text != null ? text.columnNumber : 0);
  }

  /**
   * Gets the length of the tag in characters.
   *
   * @return The tag's length
   */
  public int getLength()
  {
    return (text != null ? text.text.length() : 0);
  }

  /**
   * Get the line number.
   *
   * @return Returns the lineNumber.
   */
  public int getLineNumber()
  {
    return (text != null ? text.lineNumber : 0);
  }

  /**
   * 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;
  }

  /**
   * 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 (text != null ? text.pos : 0);
  }

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

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

  /**
   * Gets whether this is a close tag.
   *
   * @return True if this tag is a close tag
   */
  public boolean isClose()
  {
    return type == TagType.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 == TagType.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 == TagType.OPEN_CLOSE;
  }

  /**
   * 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.
   *
   * @return this
   */
  public XmlTag makeImmutable()
  {
    if (isMutable)
    {
      isMutable = false;
      if (attributes != null)
      {
        attributes.makeImmutable();
        text = null;
      }
    }
    return this;
  }

  /**
   * 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(final XmlTag dest)
  {
    dest.namespace = namespace;
    dest.name = name;
    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<String, Object> map)
  {
    for (final Map.Entry<String, Object> entry : map.entrySet())
    {
      Object value = entry.getValue();
      put(entry.getKey(), (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.intern();
    }
    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 != null ? namespace.intern() : null;
    }
    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)
  {
    closes = tag;
  }

  /**
   * Sets type of this tag if it is not immutable.
   *
   * @param type
   *            The new type
   */
  public void setType(final TagType 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 = " + text.pos + ", line = " + text.lineNumber +
      ", attributes = [" + getAttributes() + "], type = " + type + "]";
  }

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

  /**
   * @return The string representation of the tag
   */
  public CharSequence toCharSequence()
  {
    if (!isMutable && (text != null))
    {
      return text.text;
    }

    return toXmlString(null);
  }

  /**
   * String representation with line and column number
   *
   * @return String version of this object
   */
  public String toUserDebugString()
  {
    return " '" + toString() + "' (line " + getLineNumber() + ", column " + getColumnNumber() +
      ")";
  }

  /**
   * 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 == TagType.CLOSE)
    {
      buffer.append('/');
    }

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

    buffer.append(name);

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

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

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

    buffer.append('>');
    return buffer;
  }

  static class TextSegment
  {
    /** Column number. */
    final int columnNumber;

    /** Line number. */
    final int lineNumber;

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

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

    TextSegment(final CharSequence text, final int pos, final int line, final int col)
    {
      this.text = text;
      this.pos = pos;
      lineNumber = line;
      columnNumber = col;
    }

    /**
     *
     * @return The xml markup text
     */
    public final CharSequence getText()
    {
      return text;
    }

    /**
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString()
    {
      return text.toString();
    }
  }
}
TOP

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

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.