sirius.kernel.xml.AbstractStructuredOutput Maven / Gradle / Ivy
/*
* Made with all the love in the world
* by scireum in Remshalden, Germany
*
* Copyright by scireum GmbH
* http://www.scireum.de - [email protected]
*/
package sirius.kernel.xml;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.BiConsumer;
/**
* Basic implementation of StructuredOutput, taking care of all output independent boilerplate code.
*/
public abstract class AbstractStructuredOutput implements StructuredOutput {
protected List nesting = new ArrayList<>();
/**
* Types used by internal bookkeeping
*/
protected enum ElementType {
UNKNOWN, OBJECT, ARRAY
}
/**
* Used by internal bookkeeping, to close elements property
*/
protected static class Element {
private ElementType type;
private String name;
private boolean empty = true;
protected Element(ElementType type, String name) {
this.type = type;
this.name = name;
}
protected boolean isEmpty() {
return empty;
}
protected void setEmpty(boolean empty) {
this.empty = empty;
}
protected ElementType getType() {
return type;
}
protected String getName() {
return name;
}
}
/**
* Returns the type of the current element.
*
* @return the type of the current element
*/
protected ElementType getCurrentType() {
if (nesting.isEmpty()) {
return ElementType.UNKNOWN;
}
return nesting.get(0).getType();
}
/**
* Determines whether the current element is empty
*
* @return true if the current element has no content or children, false otherwise
*/
public boolean isCurrentObjectEmpty() {
if (nesting.isEmpty()) {
return true;
}
return nesting.get(0).isEmpty();
}
@Override
public StructuredOutput beginArray(String name) {
startArray(name);
if (!nesting.isEmpty()) {
nesting.get(0).setEmpty(false);
}
nesting.add(0, new Element(ElementType.ARRAY, name));
return this;
}
@Override
public StructuredOutput array(@Nonnull String name, @Nonnull String elementName, @Nonnull Collection> array) {
beginArray(name);
for (Object o : array) {
property(elementName, o);
}
endArray();
return this;
}
@Override
public StructuredOutput array(@Nonnull String name,
@Nonnull Collection array,
BiConsumer arrayConsumer) {
beginArray(name);
for (E e : array) {
arrayConsumer.accept(this, e);
}
endArray();
return this;
}
/**
* Must be implemented by subclasses to start a new array.
*
* @param name the name of the array property.
*/
protected abstract void startArray(String name);
/**
* Must be implemented by subclasses to start a new object.
*
* @param name the name of the object
* @param attributes the attributes of the object
*/
protected abstract void startObject(String name, Attribute... attributes);
/**
* Must be implemented by subclasses to end an array.
*
* @param name the name of the array property to close
*/
protected abstract void endArray(String name);
/**
* Must be implemented by subclasses to end an object.
*
* @param name the name of the object to close
*/
protected abstract void endObject(String name);
/**
* Must be implemented by subclasses to generate a property.
*
* @param name the name of the property
* @param value the value of the property
*/
protected abstract void writeProperty(String name, Object value);
@Override
public StructuredOutput beginObject(String name) {
startObject(name, (Attribute[]) null);
if (!nesting.isEmpty()) {
nesting.get(0).setEmpty(false);
}
nesting.add(0, new Element(ElementType.OBJECT, name));
return this;
}
@Override
public StructuredOutput beginObject(String name, Attribute... attributes) {
startObject(name, attributes);
if (!nesting.isEmpty()) {
nesting.get(0).setEmpty(false);
}
nesting.add(0, new Element(ElementType.OBJECT, name));
return this;
}
/**
* Used to fluently create a {@link #beginObject(String, Attribute...)}.
*/
public class TagBuilder {
private List attributes = new ArrayList<>();
private String name;
/**
* Creates a new TabBuilder with the given tag name
*
* @param name the name of the resulting tag
*/
public TagBuilder(String name) {
this.name = name;
}
/**
* Adds an attribute to the tag
*
* @param name the name of the attribute to add
* @param value the value of the attribute to add
* @return this to fluently add more attributes
*/
public TagBuilder addAttribute(@Nonnull String name, @Nullable String value) {
attributes.add(Attribute.set(name, value));
return this;
}
/**
* Finally creates the tag or object with the given name and attributes.
*/
public void build() {
beginObject(name, attributes.toArray(new Attribute[attributes.size()]));
}
}
/**
* Creates a new object using the returned tag builder
*
* @param name the name of the object to create
* @return a tag builder to fluently create a new object
*/
@CheckReturnValue
public TagBuilder buildObject(@Nonnull String name) {
return new TagBuilder(name);
}
@Override
public StructuredOutput endArray() {
if (nesting.isEmpty()) {
throw new IllegalArgumentException("Invalid result structure. No array to close");
}
Element e = nesting.get(0);
nesting.remove(0);
if (e.getType() != ElementType.ARRAY) {
throw new IllegalArgumentException("Invalid result structure. No array to close");
}
endArray(e.getName());
return this;
}
@Override
public StructuredOutput endObject() {
if (nesting.isEmpty()) {
throw new IllegalArgumentException("Invalid result structure. No object to close");
}
Element e = nesting.get(0);
nesting.remove(0);
if (e.getType() != ElementType.OBJECT) {
throw new IllegalArgumentException("Invalid result structure. No object to close");
}
endObject(e.getName());
return this;
}
@Override
public void endResult() {
if (!nesting.isEmpty()) {
throw new IllegalArgumentException("Invalid result structure. Cannot close result. Objects are still "
+ "open"
+ ".");
}
}
@Override
public StructuredOutput property(String name, Object data) {
if (getCurrentType() != ElementType.OBJECT && getCurrentType() != ElementType.ARRAY) {
throw new IllegalArgumentException("Invalid result structure. Cannot place a property here.");
}
writeProperty(name, data);
nesting.get(0).setEmpty(false);
return this;
}
@Override
public StructuredOutput nullsafeProperty(@Nonnull String name, @Nullable Object data) {
property(name, data != null ? data : "");
return this;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy