/*
* IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
*
* http://izpack.org/
* http://izpack.codehaus.org/
*
* Copyright (c) 2008, 2009 Anthonin Bonnefoy
* Copyright (c) 2008, 2009 David Duponchel
*
* Licensed 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 com.izforge.izpack.adaptator.impl;
import com.izforge.izpack.adaptator.IXMLElement;
import org.w3c.dom.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;
import java.util.regex.Pattern;
/**
* Implementation of the adaptator between nanoXml and javax
*
* @author Anthonin Bonnefoy
* @author David Duponchel
*/
public class XMLElementImpl implements IXMLElement
{
/**
* The dom element embedded by the XMLElement
*/
private Element element;
/**
* A flag to notice any changement made to the element.
* It is used to generate the childrenVector.
*/
private boolean hasChanged = true;
/**
* Vector of the children elements.
* It is generated as it is called.
*/
private Vector<IXMLElement> childrenVector;
/**
* Create a new root element in a new document.
*
* @param name Name of the root element
*/
public XMLElementImpl(String name)
{
Document document;
try
{
// Création d'un nouveau DOM
DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder constructeur = documentFactory.newDocumentBuilder();
document = constructeur.newDocument();
// Propriétés du DOM
document.setXmlVersion("1.0");
element = document.createElement(name);
document.appendChild(element);
} catch (ParserConfigurationException e)
{
e.printStackTrace();
}
}
/**
* Constructor which create a root element in the given document
*
* @param name Name of the root element
* @param inDocument The document in which to create the xml
*/
public XMLElementImpl(String name, Document inDocument)
{
element = inDocument.createElement(name);
}
/**
* Create a element in the same document of the given element
*
* @param name Name of the element
* @param elementReference Reference of an existing xml. It is used to generate an xmlElement on the same document.
*/
public XMLElementImpl(String name, IXMLElement elementReference)
{
element = elementReference.getElement().getOwnerDocument().createElement(name);
}
/**
* Constructor saving the passed node
*
* @param node Node to save inside the XMLElement
*/
public XMLElementImpl(Node node)
{
if (!(node instanceof Element))
{
throw new IllegalArgumentException("The node should be an instance of Element");
}
this.element = (Element) node;
}
public String getName()
{
return element.getNodeName();
}
public void addChild(IXMLElement child)
{
hasChanged = true;
element.appendChild(child.getElement());
}
public void removeChild(IXMLElement child)
{
hasChanged = true;
element.removeChild(child.getElement());
}
public boolean hasChildren()
{
for (Node child = element.getFirstChild(); child != null; child = child.getNextSibling())
{
if (child.getNodeType() == Node.ELEMENT_NODE)
{
return true;
}
}
return false;
}
private void initChildrenVector()
{
if (hasChanged)
{
hasChanged = false;
childrenVector = new Vector<IXMLElement>();
for (Node child = element.getFirstChild(); child != null; child = child.getNextSibling())
{
if (child.getNodeType() == Node.ELEMENT_NODE)
{
childrenVector.add(new XMLElementImpl(child));
}
}
}
}
public int getChildrenCount()
{
initChildrenVector();
return childrenVector.size();
}
public Vector<IXMLElement> getChildren()
{
initChildrenVector();
return childrenVector;
}
public IXMLElement getChildAtIndex(int index)
{
initChildrenVector();
return childrenVector.get(index);
}
public IXMLElement getFirstChildNamed(String name)
{
XMLElementImpl res = null;
NodeList nodeList = element.getElementsByTagName(name);
if (nodeList.getLength() > 0)
{
res = new XMLElementImpl(nodeList.item(0));
}
return res;
}
public Vector<IXMLElement> getChildrenNamed(String name)
{
Vector<IXMLElement> res = new Vector<IXMLElement>();
NodeList nodeList = element.getElementsByTagName(name);
Element child;
for (int i = 0; i < nodeList.getLength(); i++)
{
child = (Element) nodeList.item(i);
res.add(new XMLElementImpl(child));
}
return res;
}
public String getAttribute(String name)
{
return this.getAttribute(name, null);
}
public String getAttribute(String name, String defaultValue)
{
Node attribute = element.getAttributes().getNamedItem(name);
if (attribute != null)
{
return attribute.getNodeValue();
}
return defaultValue;
}
public void setAttribute(String name, String value)
{
NamedNodeMap attributes = element.getAttributes();
Attr attribute = element.getOwnerDocument().createAttribute(name);
attribute.setValue(value);
attributes.setNamedItem(attribute);
}
public void removeAttribute(String name)
{
this.element.getAttributes().removeNamedItem(name);
}
public Enumeration enumerateAttributeNames()
{
NamedNodeMap namedNodeMap = element.getAttributes();
Properties properties = new Properties();
for (int i = 0; i < namedNodeMap.getLength(); i++)
{
Node node = namedNodeMap.item(i);
properties.put(node.getNodeName(), node.getNodeValue());
}
return properties.keys();
}
public boolean hasAttribute(String name)
{
return (this.element.getAttributes().getNamedItem(name) != null);
}
public Properties getAttributes()
{
Properties properties = new Properties();
NamedNodeMap namedNodeMap = this.element.getAttributes();
for (int i = 0; i < namedNodeMap.getLength(); ++i)
{
properties.put(namedNodeMap.item(i).getNodeName(), namedNodeMap.item(i).getNodeValue());
}
return properties;
}
public int getLineNr()
{
Object ln = element.getUserData("ln");
if (ln == null)
{
return NO_LINE;
}
try
{
return (Integer) element.getUserData("ln");
}
catch (ClassCastException e)
{
return NO_LINE;
}
}
public String getContent()
{
StringBuilder sb = new StringBuilder();
String content;
Node child = this.element.getFirstChild();
// no error if there are children
boolean err = (child == null);
// pattern : only whitespace characters
Pattern p = Pattern.compile("^\\s+$");
while (!err && child != null)
{
content = child.getNodeValue();
if (child.getNodeType() == Node.TEXT_NODE)
{
// text node : nanoXML ignores it if it's only whitespace characters.
if (content != null && !p.matcher(content).matches())
{
sb.append(content);
}
}
else if (child.getNodeType() == Node.CDATA_SECTION_NODE)
{
sb.append(content);
}
// neither CDATA nor text : real nested element !
else
{
err = true;
}
child = child.getNextSibling();
}
return (err) ? null : sb.toString().trim();
}
public void setContent(String content)
{
Node child;
while ((child = this.element.getFirstChild()) != null)
{
this.element.removeChild(child);
}
element.appendChild(element.getOwnerDocument().createTextNode(content));
}
public Node getElement()
{
return element;
}
@Override
public String toString()
{
return element.getNodeName() + " " + element.getNodeValue();
}
@Override
public boolean equals(Object obj)
{
if (obj instanceof IXMLElement)
{
IXMLElement o = (IXMLElement) obj;
Element elem = (Element) o.getElement();
Node child2 = elem.getFirstChild();
for (Node child = element.getFirstChild(); child != null && child2 != null; child = child.getNextSibling())
{
if (!child.equals(child2))
{
return false;
}
child2.getNextSibling();
}
return true;
}
return false;
}
}