/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* 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:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.commons.xml;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import static org.eclipse.che.commons.xml.XMLTreeUtil.tabulate;
import static java.util.Arrays.asList;
/**
* Used to add new element to {@link XMLTree}.
* <p/>
* This class is really convenient for complex tree updates.
* To do it you need to make hierarchy from NewElement
* instances which can contain NewAttribute instances or text as well.
* When {@link NewElement} instance is ready tree uses {@link NewElement#asString()}
* to get view of new element.
* <p/>
* Why don't we just create {@link Element} instead of using {@link NewElement} class?
* <ul>
* <li>First reason - is performance!
* Each time when you need to insert element
* tree bytes should be rewrote, but with {@link NewElement}
* tree bytes will be updated only time</li>
* <li>Second reason is - data redundancy!
* Element should keep values such as children, attributes, name, text
* for each element instance which will be added to tree
* and after tree update this values must be dropped because
* element delegates for {@link org.w3c.dom.Node} and doesn't need it anymore.</li>
* <li>Third reason is - tree integrity!
* Element instance created with tree should be inserted
* into same tree, so each time when update is going we
* need to make a lot of checks to save tree elements integrity</li>
* </ul>
*
* @author Eugene Voevodin
*/
public final class NewElement extends QName {
public static NewElement createElement(String name) {
return new NewElement(name, null);
}
public static NewElement createElement(String name, String text) {
return new NewElement(name, text);
}
public static NewElement createElement(String name, NewElement... children) {
final NewElement newElement = createElement(name);
newElement.children = new ArrayList<>(asList(children));
return newElement;
}
private String text;
private List<NewAttribute> attributes;
private List<NewElement> children;
private NewElement(String name, String text) {
super(name);
this.text = text;
}
public NewElement setText(String text) {
this.text = text;
return this;
}
public NewElement setAttributes(List<NewAttribute> attributes) {
this.attributes = attributes;
return this;
}
public NewElement setChildren(List<NewElement> children) {
this.children = children;
return this;
}
public NewElement appendChild(NewElement child) {
getChildren().add(child);
return this;
}
public NewElement setAttribute(String name, String value) {
getAttributes().add(new NewAttribute(name, value));
return this;
}
public String getText() {
return text == null ? "" : text;
}
public List<NewAttribute> getAttributes() {
if (attributes == null) {
attributes = new LinkedList<>();
}
return attributes;
}
public List<NewElement> getChildren() {
if (children == null) {
children = new LinkedList<>();
}
return children;
}
public boolean hasChildren() {
return children != null && !children.isEmpty();
}
public boolean isVoid() {
return text == null && !hasChildren();
}
public String asString() {
final StringBuilder builder = new StringBuilder();
builder.append('<')
.append(getName());
if (attributes != null) {
for (NewAttribute attribute : attributes) {
builder.append(' ')
.append(attribute.asString());
}
}
//if it is void element such as <tag attr="value"/>
if (isVoid()) {
return builder.append('/')
.append('>')
.toString();
}
builder.append('>')
.append(getText());
if (hasChildren()) {
builder.append('\n');
for (NewElement child : children) {
builder.append(tabulate(child.asString(), 1))
.append('\n');
}
}
builder.append('<')
.append('/')
.append(getName())
.append('>');
return builder.toString();
}
@Override
public String toString() {
return asString();
}
}