javax0.jamal.yaml.Xml Maven / Gradle / Ivy
package javax0.jamal.yaml;
import javax0.jamal.api.BadSyntax;
import javax0.jamal.api.InnerScopeDependent;
import javax0.jamal.api.Input;
import javax0.jamal.api.Macro;
import javax0.jamal.api.Processor;
import javax0.jamal.api.Xml.ATTR;
import javax0.jamal.api.Xml.CDATA;
import javax0.jamal.api.Xml.CDATATEXT;
import javax0.jamal.api.Xml.TAG;
import javax0.jamal.api.Xml.TEXT;
import javax0.jamal.engine.StackLimiter;
import javax0.jamal.tools.Params;
import java.util.List;
import java.util.Map;
@Macro.Stateful
public class Xml implements Macro, InnerScopeDependent {
@Override
public String evaluate(Input in, Processor processor) throws BadSyntax {
final var clone = Resolver.cloneOption();
final var copy = Resolver.copyOption();
final var topTag = Params.holder("yamlXmlTopTag", "tag").orElse("xml");
final var attributes = Params.holder("yamlXmlAttributes", "attributes").orElseNull();
Params.using(processor).from(this).keys(clone, copy, topTag, attributes).between("()").parse(in);
final var yamlObject = Resolve.getYaml(processor, in.toString().trim());
Resolver.resolve(yamlObject, processor, clone.is(), copy.is());
//TODO create a new instance to make it thread safe
return toXml(topTag.get(), attributes.get(), yamlObject.getObject());
}
@Override
public String getId() {
return "yaml:xml";
}
private StackLimiter stackLimiter;
public String toXml(final String topTag, String attributes, Object content) throws BadSyntax {
stackLimiter = new StackLimiter();
final var sb = new StringBuilder("");
sb.append("<").append(topTag);
if (attributes != null && attributes.length() > 0) {
sb.append(" ").append(attributes);
}
if (content instanceof Map, ?>) {
mapToXml(sb, (Map) content);
} else if (content instanceof List>) {
listToXml(sb, topTag, topTag.substring(0, topTag.length() - 1), (List) content);
} else {
throw new BadSyntax("You can only convert Map or List structures to XML.");
}
sb.append("").append(topTag).append(">");
return sb.toString();
}
private void mapToXml(StringBuilder sb, Map map) throws BadSyntax {
stackLimiter.up();
boolean closed = false;
boolean ended = false;
for (final var e : map.entrySet()) {
final var tag = e.getKey();
final var value = e.getValue();
if (value instanceof ATTR) {
BadSyntax.when(closed, "!text cannot follow content node");
final var attrs = (ATTR) value;
if (attrs.size() != 0) {
attributesTo(sb, attrs);
} else {
sb.append(" ").append(tag).append("=\"").append(escape(value.toString())).append("\"");
}
} else {
if (!closed) {
sb.append(">");
closed = true;
}
if (value instanceof TEXT) {
sb.append(escape(""+value));
ended = true;
} else if (value instanceof CDATATEXT) {
sb.append("");
ended = true;
} else if (value instanceof CDATA) {
sb.append("<").append(tag).append(">");
sb.append("");
} else if (value instanceof Map, ?>) {
sb.append("<").append(tag);
mapToXml(sb, (Map) value);
} else if (value instanceof List>) {
sb.append("<").append(tag);
listToXml(sb, tag, tag.substring(0, tag.length() - 1), (List) value);
} else {
sb.append("<").append(tag).append(">");
sb.append(escape(""+value));
}
}
if (closed && !ended) {
sb.append("").append(tag).append(">");
}
}
stackLimiter.down();
}
private void listToXml(StringBuilder sb, String tagPlural, String tagSingular, List list) throws BadSyntax {
stackLimiter.up();
boolean tagged = false;
boolean closed = false;
for (final var e : list) {
if (e instanceof ATTR) {
BadSyntax.when(closed, "!attr cannot follow content node.");
attributesTo(sb, (Map) e);
} else if (e instanceof TAG) {
BadSyntax.when(closed, "!tag cannot follow content node.");
BadSyntax.when(tagged, "!tag must not be repeated.");
tagged = true;
tagSingular = ((TAG) e).id;
} else {
if (!closed) {
sb.append(">");
closed = true;
}
BadSyntax.when(tagSingular.length() == 0, "Cannot create aní XML list for the field '%s' it is too short and no !tag was present.", tagPlural);
sb.append("<").append(tagSingular);
if (e instanceof Map, ?>) {
mapToXml(sb, (Map) e);
} else if (e instanceof List>) {
final String ts = tagSingular;
BadSyntax.when(tagSingular.length() < 2, "Cannot create an XML list for the field '%s'", ts);
listToXml(sb, tagSingular, tagSingular.substring(0, tagSingular.length() - 1), (List) e);
} else if (e instanceof CDATA) {
sb.append(">");
sb.append("");
} else {
sb.append(">");
sb.append(escape(e.toString()));
}
sb.append("").append(tagSingular).append(">");
}
}
stackLimiter.down();
}
private void attributesTo(StringBuilder sb, Map attrs) {
for (final var attr : attrs.entrySet()) {
sb.append(" ").append(attr.getKey()).append("=\"").append(escape(attr.getValue())).append("\"");
}
}
private static final Map ESCHMAP = Map.of('"', """, '\'', "'", '<', "<", '>', ">", '&', "&");
private String escape(String in) {
final var sb = new StringBuilder();
for (final var ch : in.toCharArray()) {
if (ESCHMAP.containsKey(ch)) {
sb.append(ESCHMAP.get(ch));
} else {
sb.append(ch);
}
}
return sb.toString();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy