blob: 5dfc6bd038bdef4b454f9701525691d85064a27e [file] [log] [blame]
/*
JSPWiki - a JSP-based WikiWiki clone.
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.wiki.ui.migrator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Node implementation representing an HTML tag, XML tag or JSP tag.
*/
public class Tag extends AbstractNode
{
private List<Attribute> m_attributes = new ArrayList<Attribute>();
/**
* Constructs a new Tag.
* @param doc the parent JspDocument
* @param type the node type
*/
public Tag( JspDocument doc, NodeType type )
{
super( doc, type );
}
/**
* Adds an attribute to the Tag.
* @param attribute the attribute to add
*/
public void addAttribute( Attribute attribute )
{
m_attributes.add( attribute );
}
/**
* {@inheritDoc} If the Node is of type
* {@link NodeType#EMPTY_ELEMENT_TAG}, the tag will be split into two nodes
* (start tag and end tag), with the child Node inserted between the two.
*
* @param node the node to insert
* @throws IllegalStateException if the current Node must be split, and does
* not have a parent.
*/
public void addChild( Node node )
{
if( m_children.size() == 0 )
{
addChild( node, 0 );
}
else
{
super.addChild( node );
}
}
/**
* {@inheritDoc} If the Node is of type {@link NodeType#EMPTY_ELEMENT_TAG},
* the tag will be split into two nodes (start tag and end tag), with the
* child Node inserted between the two.
*
* @param node the node to insert
* @param index the position to insert the Node into
* @throws IllegalStateException if the current Node must be split, and does
* not have a parent.
*/
public void addChild( Node node, int index )
{
// If this node is an "empty element node," split it into two
if( m_type == NodeType.EMPTY_ELEMENT_TAG )
{
if( m_parent == null )
{
throw new IllegalStateException( "Node does not have a parent!" );
}
// Change node type to start tag
m_type = NodeType.START_TAG;
// Build new end tag & set its parent
Tag endNode = new Tag( m_doc, NodeType.END_TAG );
endNode.setName( m_name );
endNode.setParent( m_parent );
// Insert as sibling of this node
List<Node> siblings = m_parent.getChildren();
int startTagPos = siblings.indexOf( this );
if( startTagPos == siblings.size() - 1 )
{
m_parent.addChild( endNode );
}
else
{
m_parent.addChild( endNode, startTagPos + 1 );
}
}
// Finally add the child to the parent
super.addChild( node, index );
}
/**
* Returns the attribute whose name matches a supplied string, or
* <code>null</code> if not found.
*
* @param name the named attribute to search for
* @return the attribute if found, or <code>null</code>
*/
public Attribute getAttribute( String name )
{
if( name == null )
{
throw new IllegalArgumentException( "Name cannot be null. " );
}
for( Attribute attribute : m_attributes )
{
if( name.equals( attribute.getName() ) )
{
return attribute;
}
}
return null;
}
/**
* Returns an unmodifiable copy of the attributes of this Tag.
*
* @return the list of attributes
*/
public List<Attribute> getAttributes()
{
List<Attribute> attributesCopy = new ArrayList<Attribute>();
attributesCopy.addAll( m_attributes );
return Collections.unmodifiableList( attributesCopy );
}
/**
* Returns the values of all child nodes, concatenated, if the Tag is a start tag; <code>null</code> otherwise.
* @return the Tag value nodes
*/
public String getValue()
{
if( m_type != NodeType.START_TAG )
{
return null;
}
return super.getValue();
}
/**
* Returns <code>true</code> if the Tag has an attribute with a supplied name,
* and <code>false</code> otherwise.
* @param name the attribute to search for
* @return the result
*/
public boolean hasAttribute( String name )
{
return getAttribute( name ) != null;
}
/**
* Removes an attribute from the Tag.
* @param attribute the attribute to remove
*/
public void removeAttribute( Attribute attribute )
{
m_attributes.remove( attribute );
}
/**
* Returns the string that represents the Tag, including the name and
* attributes, but not any child nodes.
*/
public String toString()
{
// Root node is easy!
if( m_type == NodeType.ROOT )
{
return "ROOT";
}
StringBuilder sb = new StringBuilder();
// Calculate start and end nodes
String tagStart = m_type.getTagStart();
String tagEnd = m_type.getTagEnd();
if( tagStart == null )
tagStart = "?";
if( tagEnd == null )
tagEnd = "?";
// Print tag start
sb.append( tagStart );
// For JSP directives, add a leading space
if ( m_type == NodeType.JSP_DIRECTIVE )
{
sb.append( ' ' );
}
// If Tag, print start/end plus attributes.
if( isHtmlNode() || m_type == NodeType.JSP_DIRECTIVE )
{
sb.append( m_name );
if( m_attributes.size() > 0 )
{
int dynamicAttributeLevels = 0;
NodeType lastType = null;
for( Attribute attr : m_attributes )
{
if( dynamicAttributeLevels == 0 )
{
sb.append( ' ' );
}
sb.append( attr.toString() );
lastType = attr.getType();
if( lastType == NodeType.DYNAMIC_ATTRIBUTE && attr.getValue().length() > 3 )
{
if( attr.getValue().charAt( 1 ) != '/' )
{
dynamicAttributeLevels++;
}
else if ( attr.getValue().charAt( attr.getValue().length() - 2 ) != '/' )
{
dynamicAttributeLevels--;
}
}
}
if( lastType == NodeType.DYNAMIC_ATTRIBUTE || m_type == NodeType.JSP_DIRECTIVE || m_type == NodeType.EMPTY_ELEMENT_TAG )
{
sb.append( ' ' );
}
}
}
// Everything else is just the start/end tags plus the children nodes
else
{
for( Node child : m_children )
{
sb.append( child.toString() );
}
}
// Print tag end
sb.append( tagEnd );
return sb.toString();
}
}