/******************************************************************************
* Copyright (c) 2014 Oracle
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Konstantin Komissarchik - initial implementation and ongoing maintenance
* Ling Hao - [348011] NPE when clearing element text
******************************************************************************/
package org.eclipse.sapphire.modeling.xml;
import static org.eclipse.sapphire.modeling.util.MiscUtil.normalizeToNull;
import static org.eclipse.sapphire.modeling.xml.XmlUtil.EMPTY_STRING;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import org.eclipse.sapphire.modeling.xml.schema.XmlContentModel;
import org.eclipse.sapphire.modeling.xml.schema.XmlDocumentSchema;
import org.eclipse.sapphire.modeling.xml.schema.XmlDocumentSchemasCache;
import org.eclipse.sapphire.modeling.xml.schema.XmlElementDefinition;
import org.eclipse.sapphire.util.IdentityCache;
import org.eclipse.sapphire.util.ListFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
/**
* @author <a href="mailto:konstantin.komissarchik@oracle.com">Konstantin Komissarchik</a>
*/
public final class XmlElement extends XmlNode
{
private final IdentityCache<Element,XmlElement> elementsCache = new IdentityCache<Element,XmlElement>();
private final IdentityCache<Attr,XmlAttribute> attributesCache = new IdentityCache<Attr,XmlAttribute>();
private final IdentityCache<Comment,XmlComment> commentsCache = new IdentityCache<Comment,XmlComment>();
private final IdentityCache<Comment,XmlMetaComment> metaCommentsCache = new IdentityCache<Comment,XmlMetaComment>();
private QName qname;
private XmlContentModel contentModel;
private boolean contentModelInitialized;
public XmlElement( final XmlResourceStore store,
final Element domElement )
{
this( store, null, domElement );
}
public XmlElement( final XmlElement parent,
final Element domElement )
{
this( parent.getResourceStore(), parent, domElement );
}
private XmlElement( final XmlResourceStore store,
final XmlElement parent,
final Element domElement )
{
super( store, parent, domElement );
this.qname = null;
this.contentModel = null;
this.contentModelInitialized = false;
}
@Override
public Element getDomNode()
{
return (Element) super.getDomNode();
}
public String getLocalName()
{
return getDomNode().getLocalName();
}
public String getNamespace()
{
return getDomNode().getNamespaceURI();
}
public QName getQualifiedName()
{
if( this.qname == null )
{
this.qname = new QName( getNamespace(), getLocalName() );
}
return this.qname;
}
public String getSchemaLocation()
{
final String namespace = getNamespace();
if( namespace != null )
{
final Element root = getDomNode().getOwnerDocument().getDocumentElement();
final NamedNodeMap attributes = root.getAttributes();
for( int i = 0, n = attributes.getLength(); i < n; i++ )
{
final Attr attr = (Attr) attributes.item( i );
final String attrLocalName = attr.getLocalName();
if( attrLocalName != null && attrLocalName.equals( "schemaLocation" ) )
{
final String[] segments = attr.getValue().split( "[\\s]+" );
boolean grabNextSegment = false;
for( int j = 0, m = segments.length; j < m; j++ )
{
if( j % 2 == 0 )
{
if( segments[ j ].equals( namespace ) )
{
grabNextSegment = true;
}
}
else
{
if( grabNextSegment )
{
return segments[ j ];
}
}
}
return namespace;
}
}
return namespace;
}
return null;
}
public XmlContentModel getContentModel()
{
if( ! this.contentModelInitialized )
{
final XmlElement parent = getParent();
if( parent == null )
{
String referer = null;
String publicId = null;
String systemId = getSchemaLocation();
final File file = getResourceStore().adapt( File.class );
if( file != null )
{
referer = file.getAbsolutePath();
}
if( systemId == null )
{
final DocumentType type = getDomNode().getOwnerDocument().getDoctype();
if( type != null )
{
publicId = type.getPublicId();
systemId = type.getSystemId();
}
}
if( publicId != null || systemId != null )
{
final XmlDocumentSchema xmlDocumentSchema = XmlDocumentSchemasCache.getSchema( referer, publicId, systemId );
if( xmlDocumentSchema != null )
{
final XmlElementDefinition xmlElementDefinition = xmlDocumentSchema.getElement( getLocalName() );
if( xmlElementDefinition != null )
{
this.contentModel = xmlElementDefinition.getContentModel();
}
}
}
}
else
{
final XmlContentModel parentXmlContentModel = parent.getContentModel();
if( parentXmlContentModel != null )
{
this.contentModel = parentXmlContentModel.findChildElementContentModel( getQualifiedName() );
}
}
this.contentModelInitialized = true;
}
return this.contentModel;
}
@Override
public String getText()
{
final NodeList nodes = getDomNode().getChildNodes();
String str = null;
StringBuilder buf = null;
for( int i = 0, n = nodes.getLength(); i < n; i++ )
{
final Node node = nodes.item( i );
final int nodeType = node.getNodeType();
if( nodeType == Node.TEXT_NODE || nodeType == Node.CDATA_SECTION_NODE )
{
final String val = node.getNodeValue();
if( buf != null )
{
buf.append( val );
}
else if( str != null )
{
buf = new StringBuilder();
buf.append( str );
buf.append( val );
str = null;
}
else
{
str = val;
}
}
}
if( buf != null )
{
return buf.toString();
}
else
{
return ( str != null ? str : EMPTY_STRING );
}
}
@Override
public void setText( String elementText )
{
validateEdit();
if( elementText == null )
{
elementText = EMPTY_STRING;
}
final boolean isElementTextNonEmpty = ( elementText.length() > 0 );
final Element domElement = getDomNode();
final NodeList elementChildren = domElement.getChildNodes();
final List<Node> removeList = new ArrayList<Node>();
Text text = null;
for( int i = 0, n = elementChildren.getLength(); i < n; i++ )
{
final Node child = elementChildren.item( i );
if( text == null && child.getNodeType() == Node.TEXT_NODE && isElementTextNonEmpty )
{
text = (Text) child;
}
else
{
removeList.add( child );
}
}
for ( Node child : removeList )
{
domElement.removeChild( child );
}
if( isElementTextNonEmpty )
{
if( text != null )
{
text.setData( elementText );
}
else
{
domElement.appendChild( domElement.getOwnerDocument().createTextNode( elementText ) );
}
}
}
public List<XmlAttribute> getAttributes()
{
final ListFactory<XmlAttribute> result = ListFactory.start();
final NamedNodeMap attributes = getDomNode().getAttributes();
this.attributesCache.track();
for( int i = 0, count = attributes.getLength(); i < count; i++ )
{
final Attr attribute = (Attr) attributes.item( i );
XmlAttribute xmlAttribute = this.attributesCache.get( attribute );
if( xmlAttribute == null )
{
xmlAttribute = new XmlAttribute( this, attribute );
this.attributesCache.put( attribute, xmlAttribute );
}
result.add( xmlAttribute );
}
this.attributesCache.purge();
return result.result();
}
public XmlAttribute getAttribute( final String name,
final boolean createIfNecessary )
{
XmlAttribute attribute = null;
for( XmlAttribute attr : getAttributes() )
{
if( equal( attr.getLocalName(), name ) )
{
attribute = attr;
break;
}
}
if( attribute == null && createIfNecessary )
{
validateEdit();
final Element domElement = getDomNode();
final Attr attr = domElement.getOwnerDocument().createAttribute( name );
domElement.setAttributeNode( attr );
attribute = new XmlAttribute( this, attr );
this.attributesCache.put( attr, attribute );
}
return attribute;
}
public String getAttributeText( final String name )
{
final XmlAttribute attr = getAttribute( name, false );
if( attr == null )
{
return EMPTY_STRING;
}
else
{
return attr.getText();
}
}
public void setAttributeText( final String name,
final String value,
final boolean removeIfNullOrEmpty )
{
validateEdit();
final String val = ( value == null ? EMPTY_STRING : value );
if( val.length() == 0 )
{
final XmlAttribute attr = getAttribute( name, false );
if( attr != null)
{
if (removeIfNullOrEmpty)
{
attr.remove();
}
else
{
getAttribute( name, true ).setText( val );
}
}
}
else
{
getAttribute( name, true ).setText( val );
}
}
public List<XmlElement> getChildElements()
{
final ListFactory<XmlElement> result = ListFactory.start();
final NodeList children = getDomNode().getChildNodes();
this.elementsCache.track();
for( int i = 0, count = children.getLength(); i < count; i++ )
{
final Node n = children.item( i );
if( n.getNodeType() == Node.ELEMENT_NODE )
{
final Element element = (Element) n;
XmlElement xmlElement = this.elementsCache.get( element );
if( xmlElement == null )
{
xmlElement = new XmlElement( this, element );
this.elementsCache.put( element, xmlElement );
}
result.add( xmlElement );
}
}
this.elementsCache.purge();
return result.result();
}
public List<XmlElement> getChildElements( final QName name )
{
final ListFactory<XmlElement> result = ListFactory.start();
for( XmlElement element : getChildElements() )
{
if( equal( normalizeToNull( element.getNamespace() ), normalizeToNull( name.getNamespaceURI() ) ) &&
equal( element.getLocalName(), name.getLocalPart() ) )
{
result.add( element );
}
}
return result.result();
}
public List<XmlElement> getChildElements( final String name )
{
return getChildElements( createQualifiedName( name ) );
}
public XmlElement getChildElement( final Element element )
{
if( element.getParentNode() != getDomNode() )
{
throw new IllegalArgumentException();
}
XmlElement xmlElement = this.elementsCache.get( element );
if( xmlElement == null )
{
xmlElement = new XmlElement( this, element );
this.elementsCache.put( element, xmlElement );
}
return xmlElement;
}
public XmlElement getChildElement( final QName name,
final boolean createIfNecessary )
{
for( XmlElement element : getChildElements() )
{
if( equal( normalizeToNull( element.getNamespace() ), normalizeToNull( name.getNamespaceURI() ) ) &&
equal( element.getLocalName(), name.getLocalPart() ) )
{
return element;
}
}
if( createIfNecessary )
{
return addChildElement( name );
}
else
{
return null;
}
}
public XmlElement getChildElement( final String name,
final boolean createIfNecessary )
{
return getChildElement( createQualifiedName( name ), createIfNecessary );
}
public XmlElement addChildElement( final QName name )
{
return addChildElement( name, (Node) null );
}
public XmlElement addChildElement( final String name )
{
return addChildElement( createQualifiedName( name ) );
}
public XmlElement addChildElement( final QName name,
final XmlElement refElement )
{
return addChildElement( name, ( refElement == null ? null : refElement.getDomNode() ) );
}
public XmlElement addChildElement( final String name,
final XmlElement refElement )
{
return addChildElement( createQualifiedName( name ), refElement );
}
private XmlElement addChildElement( final QName name,
final Node refNode )
{
validateEdit();
final Element domElement = getDomNode();
final Document document = domElement.getOwnerDocument();
final NodeList siblings = domElement.getChildNodes();
final int siblingsCount = siblings.getLength();
// Notify listeners that an element is about to be added.
notifyPreChildElementAddListeners();
// Create the new element and insert it in the correct spot.
Node refNodeRevised = refNode;
if( refNodeRevised == null )
{
final XmlContentModel xmlContentModel = getContentModel();
if( xmlContentModel != null )
{
final int position = xmlContentModel.findInsertionPosition( siblings, name );
if( position < siblingsCount )
{
refNodeRevised = siblings.item( position );
}
}
}
Node prevNode = null;
if( refNodeRevised == null )
{
if( siblingsCount > 0 )
{
prevNode = siblings.item( siblingsCount - 1 );
}
}
else
{
prevNode = refNodeRevised.getPreviousSibling();
}
if( prevNode != null && prevNode.getNodeType() == Node.TEXT_NODE &&
prevNode.getNodeValue().trim().length() == 0 )
{
refNodeRevised = prevNode;
}
final String namespace = name.getNamespaceURI();
final Element element;
if( namespace == null || namespace.length() == 0 )
{
element = document.createElementNS( null, name.getLocalPart() );
}
else
{
final String prefix = findNamespacePrefix( namespace, name.getPrefix() );
final String qname
= ( ( prefix == null || prefix.length() == 0 )
? name.getLocalPart()
: prefix + ":" + name.getLocalPart() );
element = document.createElementNS( namespace, qname );
}
domElement.insertBefore( element, refNodeRevised );
final XmlElement wrappedElement = new XmlElement( this, element );
this.elementsCache.put( element, wrappedElement );
if( domElement.getNodeType() == Node.ELEMENT_NODE && domElement.getChildNodes().getLength() == 1 )
{
format();
}
else
{
wrappedElement.format();
}
// Notify listeners that an element has been added.
notifyPostChildElementAddListeners();
// Return new element to caller.
return wrappedElement;
}
private String findNamespacePrefix( final String namespace,
final String defaultPrefix )
{
final Element domElement = getDomNode();
String prefix = null;
if( namespace != null && namespace.length() > 0 )
{
final String ns = getNamespace();
if( ns != null && ns.equals( namespace ) )
{
prefix = domElement.getPrefix();
}
else
{
final Map<String,String> namespaces = new HashMap<String,String>();
Element el = domElement;
boolean found = false;
while( el != null && ! found )
{
final NamedNodeMap attributes = el.getAttributes();
for( int i = 0, n = attributes.getLength(); i < n; i++ )
{
final Attr attr = (Attr) attributes.item( i );
final String attrName = attr.getName();
final String attrValue = attr.getValue();
if( attrName.equals( "xmlns" ) )
{
if( attrValue.equals( namespace ) )
{
found = true;
break;
}
namespaces.put( "", attrValue );
}
else if( attrName.startsWith( "xmlns:" ) )
{
final String p = attrName.substring( 6 );
if( attrValue.equals( namespace ) )
{
prefix = p;
found = true;
break;
}
namespaces.put( p, attrValue );
}
}
final Node parent = el.getParentNode();
if( parent instanceof Element )
{
el = (Element) parent;
}
else
{
el = null;
}
}
if( ! found )
{
prefix = defaultPrefix;
for( int i = 1; namespaces.containsKey( prefix ); i++ )
{
prefix = defaultPrefix + String.valueOf( i );
}
final String xmlnsAttrName = "xmlns:" + prefix;
final Element root = domElement.getOwnerDocument().getDocumentElement();
root.setAttribute( xmlnsAttrName, namespace );
if( ! namespace.equals( "http://www.w3.org/2001/XMLSchema-instance" ) )
{
final XmlContentModel xmlContentModel = getContentModel();
String schemaLocation = null;
if( xmlContentModel != null )
{
schemaLocation = xmlContentModel.getSchema().getSchemaLocation( namespace );
}
addSchemaLocation( namespace, schemaLocation );
}
}
}
}
return prefix;
}
private void addSchemaLocation( final String namespace,
final String schemaLocation )
{
if( schemaLocation != null && ! namespace.equals( schemaLocation ) )
{
final String xsiNamespacePrefix
= findNamespacePrefix( "http://www.w3.org/2001/XMLSchema-instance", "xsi" );
final String schemaLocationAttrName
= ( xsiNamespacePrefix == null ? "schemaLocation" : xsiNamespacePrefix + ":schemaLocation" );
final Element root = getDomNode().getOwnerDocument().getDocumentElement();
final String existingSchemaLocations = root.getAttribute( schemaLocationAttrName );
final Map<String,String> schemaLocations = new LinkedHashMap<String,String>();
if( existingSchemaLocations != null && existingSchemaLocations.length() > 0 )
{
String ns = null;
for( String segment : existingSchemaLocations.split( "[\\s]+" ) )
{
if( ns == null )
{
ns = segment;
}
else
{
schemaLocations.put( ns, segment );
ns = null;
}
}
}
schemaLocations.put( namespace, schemaLocation );
final StringBuilder buf = new StringBuilder();
for( Map.Entry<String,String> entry : schemaLocations.entrySet() )
{
if( buf.length() > 0 )
{
buf.append( ' ' );
}
buf.append( entry.getKey() );
buf.append( ' ' );
buf.append( entry.getValue() );
}
root.setAttribute( schemaLocationAttrName, buf.toString() );
}
}
public XmlNode getChildNode( final XmlPath path,
final boolean createIfNecessary )
{
XmlElement el = this;
for( XmlPath.Segment segment : path.getSegments() )
{
XmlNode node = el.getChildNode( segment, createIfNecessary );
if( node instanceof XmlElement )
{
el = (XmlElement) node;
}
else
{
return node;
}
}
return el;
}
public XmlNode getChildNode( final XmlPath.Segment pathSegment,
final boolean createIfNecessary )
{
final XmlPath.Segment resolvedPathSegment = resolveXmlPathSegment( pathSegment );
final QName qname = resolvedPathSegment.getQualifiedName();
if( resolvedPathSegment.isAttribute() )
{
return getAttribute( qname.getLocalPart(), createIfNecessary );
}
else if( resolvedPathSegment.isComment() )
{
return getMetaComment( qname.getLocalPart(), createIfNecessary );
}
else
{
return getChildElement( qname, createIfNecessary );
}
}
public String getChildNodeText( final XmlPath path )
{
final XmlNode node = getChildNode( path, false );
if( node != null )
{
return node.getText();
}
else
{
return EMPTY_STRING;
}
}
public String getChildNodeText( final String path )
{
return getChildNodeText( new XmlPath( path ) );
}
public void setChildNodeText( final XmlPath path,
final String text,
final boolean removeIfNullOrEmpty )
{
validateEdit();
if( removeIfNullOrEmpty && ( text == null || text.trim().length() == 0 ) && path.getSegmentCount() > 0 )
{
removeChildNode( path );
}
else
{
getChildNode( path, true ).setText( text );
}
}
public void setChildNodeText( final String path,
final String text,
final boolean removeIfNullOrEmpty )
{
setChildNodeText( new XmlPath( path ), text, removeIfNullOrEmpty );
}
public void removeChildNode( final String path )
{
removeChildNode( new XmlPath( path ) );
}
public void removeChildNode( final XmlPath path )
{
removeChildNode( this, path, 0 );
}
private void removeChildNode( final XmlElement el,
final XmlPath path,
final int pathPosition )
{
validateEdit();
final XmlPath.Segment segment = path.getSegment( pathPosition );
final XmlNode child = el.getChildNode( segment, false );
if( child != null )
{
if( pathPosition == path.getSegmentCount() - 1 )
{
child.remove();
}
else
{
if( child instanceof XmlElement )
{
final XmlElement childElement = (XmlElement) child;
removeChildNode( childElement, path, pathPosition + 1 );
if( childElement.isEmpty() )
{
childElement.remove();
}
}
}
}
}
public void move( final XmlElement ref )
{
if( this != ref )
{
validateEdit();
// In order to preserve the formatting, must move the element with all of its preceding
// whitespace. Similarly, the insertion position must be before all of the reference
// element's preceding whitespace.
final Element domElement = getDomNode();
final Node domParentNode = domElement.getParentNode();
final ListFactory<Node> domNodesToMove = ListFactory.start();
domNodesToMove.add( domElement );
for( Node prev = domElement.getPreviousSibling();
prev != null && prev.getNodeType() == Node.TEXT_NODE && prev.getNodeValue().trim().length() == 0;
prev = prev.getPreviousSibling() )
{
domNodesToMove.add( prev );
}
Node domRefNode = ref.getDomNode();
for( Node prev = domRefNode.getPreviousSibling();
prev != null && prev.getNodeType() == Node.TEXT_NODE && prev.getNodeValue().trim().length() == 0;
prev = prev.getPreviousSibling() )
{
domRefNode = prev;
}
for( Node node : domNodesToMove.result() )
{
domParentNode.removeChild( node );
domParentNode.insertBefore( node, domRefNode );
domRefNode = node;
}
}
}
public void swap( final XmlElement y )
{
validateEdit();
// It should be possible to implement this more optimally, but the
// bookmark approach yields a very simple implementation which
// is therefore easier to prove as correct in all cases.
final Element domElement = getDomNode();
final Node parent = domElement.getParentNode();
final Document document = parent.getOwnerDocument();
final Node xBookmark = document.createTextNode( EMPTY_STRING );
parent.insertBefore( xBookmark, domElement );
final Node yBookmark = document.createTextNode( EMPTY_STRING );
parent.insertBefore( yBookmark, y.getDomNode() );
parent.removeChild( domElement );
parent.removeChild( y.getDomNode() );
parent.insertBefore( domElement, yBookmark );
parent.insertBefore( y.getDomNode(), xBookmark );
parent.removeChild( xBookmark );
parent.removeChild( yBookmark );
}
@Override
public void remove()
{
validateEdit();
final Element domElement = getDomNode();
final Node parentDomNode = domElement.getParentNode();
if( parentDomNode != null )
{
final XmlElement parentXmlElement = getParent();
if( parentXmlElement != null )
{
parentXmlElement.notifyPreChildElementRemoveListeners();
}
final Node previousSibling = domElement.getPreviousSibling();
parentDomNode.removeChild( domElement );
if( previousSibling != null && previousSibling.getNodeType() == Node.TEXT_NODE &&
previousSibling.getNodeValue().trim().length() == 0 )
{
parentDomNode.removeChild( previousSibling );
}
if( parentXmlElement != null )
{
parentXmlElement.notifyPostChildElementRemoveListeners();
}
}
}
public boolean isEmpty()
{
final Node node = getDomNode();
if( node.hasAttributes() )
{
return false;
}
final NodeList children = node.getChildNodes();
for( int i = 0, n = children.getLength(); i < n; i++ )
{
final Node child = children.item( i );
if( child.getNodeType() != Node.TEXT_NODE || child.getNodeValue().trim().length() > 0 )
{
return false;
}
}
return true;
}
public List<XmlComment> getComments()
{
final ListFactory<XmlComment> result = ListFactory.start();
final NodeList children = getDomNode().getChildNodes();
this.commentsCache.track();
for( int i = 0, count = children.getLength(); i < count; i++ )
{
final Node n = children.item( i );
if( n.getNodeType() == Node.COMMENT_NODE )
{
final Comment comment = (Comment) n;
XmlComment xmlComment = this.commentsCache.get( comment );
if( xmlComment == null )
{
xmlComment = new XmlComment( this, comment );
this.commentsCache.put( comment, xmlComment );
}
result.add( xmlComment );
}
}
this.commentsCache.purge();
return result.result();
}
public XmlComment addComment( final String commentText )
{
validateEdit();
final Element domElement = getDomNode();
final Document document = domElement.getOwnerDocument();
final NodeList nodes = domElement.getChildNodes();
int position = 0;
for( int n = nodes.getLength(); position < n; position++ )
{
final Node child = nodes.item( position );
if( child.getNodeType() != Node.COMMENT_NODE && child.getNodeType() != Node.TEXT_NODE )
{
break;
}
}
Node refChild = ( position < nodes.getLength() ) ? nodes.item( position ) : null;
int prevPosition = position - 1;
Node prevChild = ( prevPosition < nodes.getLength()) ? nodes.item( prevPosition ) : null;
if( prevChild != null && prevChild.getNodeType() == Node.TEXT_NODE &&
prevChild.getNodeValue().trim().length() == 0 )
{
refChild = prevChild;
position = prevPosition;
}
prevPosition = position - 1;
prevChild = ( prevPosition < nodes.getLength()) ? nodes.item( prevPosition ) : null;
final Comment comment = document.createComment( commentText );
domElement.insertBefore( comment, refChild );
final XmlComment wrappedComment = new XmlComment( this, comment );
this.commentsCache.put( comment, wrappedComment );
if( domElement.getNodeType() == Node.ELEMENT_NODE && domElement.getChildNodes().getLength() == 1 )
{
format();
}
else
{
wrappedComment.format();
}
return wrappedComment;
}
public List<XmlMetaComment> getMetaComments()
{
final ListFactory<XmlMetaComment> result = ListFactory.start();
final NodeList children = getDomNode().getChildNodes();
this.metaCommentsCache.track();
for( int i = 0, count = children.getLength(); i < count; i++ )
{
final Node n = children.item( i );
if( n.getNodeType() == Node.COMMENT_NODE && n.getNodeValue().indexOf( ':' ) != -1 )
{
final Comment metaComment = (Comment) n;
XmlMetaComment xmlMetaComment = this.metaCommentsCache.get( metaComment );
if( xmlMetaComment == null )
{
xmlMetaComment = new XmlMetaComment( this, metaComment );
this.metaCommentsCache.put( metaComment, xmlMetaComment );
}
result.add( xmlMetaComment );
}
}
this.metaCommentsCache.purge();
return result.result();
}
public XmlMetaComment getMetaComment( final String name,
final boolean createIfNecessary )
{
XmlMetaComment xmlMetaComment = null;
for( XmlMetaComment x : getMetaComments() )
{
if( equal( x.getName(), name ) )
{
xmlMetaComment = x;
break;
}
}
if( xmlMetaComment == null && createIfNecessary )
{
final Comment comment = addComment( name + ":" ).getDomNode();
xmlMetaComment = new XmlMetaComment( this, comment );
this.metaCommentsCache.put( comment, xmlMetaComment );
}
return xmlMetaComment;
}
public String getMetaCommentText( final String name )
{
final XmlMetaComment comment = getMetaComment( name, false );
if( comment != null )
{
return comment.getText();
}
else
{
return null;
}
}
public void setMetaCommentText( final String name,
String value )
{
validateEdit();
if( value != null )
{
if( value.length() == 0 )
{
value = null;
}
}
XmlMetaComment comment = getMetaComment( name, true );
if( value != null )
{
comment.setText( value );
}
else
{
comment.remove();
}
}
private XmlPath.Segment resolveXmlPathSegment( final XmlPath.Segment pathSegment )
{
String namespace = pathSegment.getQualifiedName().getNamespaceURI();
if( pathSegment.isAttribute() || pathSegment.isComment() || namespace.length() != 0 )
{
return pathSegment;
}
else
{
final String prefix = pathSegment.getQualifiedName().getPrefix();
if( prefix.length() == 0 )
{
namespace = getDomNode().getNamespaceURI();
}
final QName newQualifiedName
= new QName( namespace, pathSegment.getQualifiedName().getLocalPart(), prefix );
return new XmlPath.Segment( newQualifiedName, false, false );
}
}
private QName createQualifiedName( final String localName )
{
final String namespace = getDomNode().getNamespaceURI();
return new QName( namespace, localName );
}
private static final boolean equal( final Object obj1,
final Object obj2 )
{
boolean objectsAreEqual = false;
if( obj1 == obj2 )
{
objectsAreEqual = true;
}
else if( obj1 != null && obj2 != null )
{
objectsAreEqual = obj1.equals( obj2 );
}
return objectsAreEqual;
}
protected final void notifyPreChildElementAddListeners()
{
notifyListeners( new Event( EventType.PRE_CHILD_ELEMENT_ADD, this ) );
}
protected final void notifyPostChildElementAddListeners()
{
notifyListeners( new Event( EventType.POST_CHILD_ELEMENT_ADD, this ) );
}
protected final void notifyPreChildElementRemoveListeners()
{
notifyListeners( new Event( EventType.PRE_CHILD_ELEMENT_REMOVE, this ) );
}
protected final void notifyPostChildElementRemoveListeners()
{
notifyListeners( new Event( EventType.POST_CHILD_ELEMENT_REMOVE, this ) );
}
}