package xapi.ui.api;
import xapi.collect.X_Collect;
import xapi.collect.api.StringTo;
import xapi.util.X_Debug;
import xapi.util.X_String;
import xapi.util.impl.LazyProvider;
import static xapi.collect.X_Collect.newStringMap;
import javax.inject.Provider;
/**
* Created by james on 16/10/15.
*/
public abstract class ElementBuilder <E> extends NodeBuilder<E> {
private static volatile int idSeed = 1;
private static final Object sync = new Object();
private int seed;
public String ensureId() {
if (X_String.isEmpty(id)) {
synchronized (sync) {
seed = idSeed++;
}
setId(generateId(seed));
}
return id;
}
public static String getDefaultPrefix() {
return System.getProperty("data.attr.prefix", "xapi-");
}
protected String generateId(int seed) {
return "ele_"+seed;
}
private String id;
public String getId() {
return id;
}
public String getId(boolean forceCreate) {
if (forceCreate) {
ensureId();
}
return id;
}
@SuppressWarnings("unused")
public abstract class StyleApplier extends AttributeBuilder implements Stylizer<NodeBuilder<E>> {
private final StringTo<AttributeBuilder> styles;
public StyleApplier() {
super("style");
styles = newStringMap(AttributeBuilder.class);
}
@Override
protected void toHtml(Appendable out) {
for (AttributeBuilder style : styles.values()) {
style.toHtml(out);
}
}
@Override
public Stylizer<NodeBuilder<E>> applyStyle(
NodeBuilder<E> element,
String key,
String value) {
if (element.el == null) {
StyleApplier attr = init();
attr.setStyle(key, value);
} else {
if (value == null) {
removeStyle(element.el, key);
} else {
setStyle(element.el, key, value);
}
}
return this;
}
public void setStyle(String key, String value) {
if (value == null) {
styles.remove(key);
} else {
init();
styles.put(key, newAttributeBuilder(key+":"+value+";"));
}
}
@SuppressWarnings("unchecked" )
private StyleApplier init() {
el = null;// reset our string so that we recompute it next time
NodeBuilder.AttributeBuilder attr = attributes.get("style");
if (attr == null) {
attributes.put("style", (attr=this));
} else {
if (!(attr instanceof ElementBuilder.StyleApplier)) {
assert false : "Only use the setStyle method to set the 'style' attribute";
throw X_Debug.recommendAssertions();
}
}
return (StyleApplier) attr;
}
protected void setValue(String value) {
E element = ElementBuilder.this.el;
if (element == null) {
init();
clearAll();
styles.clear();
addValue(value);
} else {
attributeApplier.get().setAttribute("style", value);
}
}
public void addValue(String value) {
for (String part : value.split(";")) {
String[] parts = part.trim().split(":");
assert parts.length == 2 : "Malformed style string: "+value
+"; expected format: key:value;key:value;...";
stylize(parts[0], parts[1]);
}
}
public StyleApplier stylize(String key, String value) {
E element = ElementBuilder.this.el;
if (element == null) {
setStyle(key.toLowerCase(), value);
} else {
setStyle(element, key.replace("[-]", ""), value);
}
return this;
}
protected abstract void removeStyle(E element, String key);
protected abstract void setStyle(E element, String key, String value);
public StyleApplier setDisplay(String display) {
stylize("display", display);
return this;
}
public StyleApplier setPosition(String position) {
stylize("position", position);
return this;
}
public StyleApplier setLeft(String left) {
stylize("left", left);
return this;
}
public StyleApplier setTop(String top) {
stylize("top", top);
return this;
}
public StyleApplier setBottom(String bottom) {
stylize("bottom", bottom);
return this;
}
public StyleApplier setRight(String right) {
stylize("right", right);
return this;
}
public StyleApplier setWidth(String width) {
stylize("width", width);
return this;
}
public StyleApplier setHeight(String width) {
stylize("width", width);
return this;
}
public StyleApplier setMaxWidth(String width) {
stylize("max-Width", width);
return this;
}
public StyleApplier setMaxHeight(String width) {
stylize("max-Width", width);
return this;
}
public StyleApplier setMinWidth(String width) {
stylize("min-Width", width);
return this;
}
public StyleApplier setMinHeight(String width) {
stylize("min-Width", width);
return this;
}
public StyleApplier setVerticalAlign(String vAlign) {
stylize("vertical-Align", vAlign);
return this;
}
public StyleApplier setHorizontalAlign(String hAlign) {
stylize("text-Align", hAlign);
return this;
}
public StyleApplier setOverflow(String overflow) {
stylize("overflow", overflow);
return this;
}
public StyleApplier setOverflowX(String overflowX) {
stylize("overflow-X", overflowX);
return this;
}
public StyleApplier setOverflowY(String overflowY) {
stylize("overflow-Y", overflowY);
return this;
}
public StyleApplier setMargin(String margin) {
stylize("margin", margin);
return this;
}
public StyleApplier setMarginLeft(String marginLeft) {
stylize("margin-Left", marginLeft);
return this;
}
public StyleApplier setMarginRight(String marginRight) {
stylize("margin-Right", marginRight);
return this;
}
public StyleApplier setMarginTop(String marginTop) {
stylize("margin-Top", marginTop);
return this;
}
public StyleApplier setMarginBottom(String marginBottom) {
stylize("margin-Bottom", marginBottom);
return this;
}
public StyleApplier setPadding(String padding) {
stylize("padding", padding);
return this;
}
public StyleApplier setPaddingLeft(String paddingLeft) {
stylize("padding-Left", paddingLeft);
return this;
}
public StyleApplier setPaddingRight(String paddingRight) {
stylize("padding-Right", paddingRight);
return this;
}
public StyleApplier setPaddingTop(String paddingTop) {
stylize("padding-Top", paddingTop);
return this;
}
public StyleApplier setPaddingBottom(String paddingBottom) {
stylize("padding-Bottom", paddingBottom);
return this;
}
public StyleApplier setBorderRadius(String borderRadius) {
stylize("border-Radius", borderRadius);
return this;
}
public StyleApplier setBorderTopLeftRadius(String borderTopLeftRadius) {
stylize("border-Top-Left-Radius", borderTopLeftRadius);
return this;
}
public StyleApplier setBorderTopRightRadius(String borderTopRightRadius) {
stylize("border-Top-Right-Radius", borderTopRightRadius);
return this;
}
public StyleApplier setBorderBottomLeftRadius(String borderBottomLeftRadius) {
stylize("border-Bottom-Left-Radius", borderBottomLeftRadius);
return this;
}
public StyleApplier setBorderBottomRightRadius(String borderBottomRightRadius) {
stylize("border-Bottom-Right-Radius", borderBottomRightRadius);
return this;
}
public StyleApplier setBorder(String border) {
stylize("border", border);
return this;
}
public StyleApplier setBorderLeft(String borderLeft) {
stylize("border-Left", borderLeft);
return this;
}
public StyleApplier setBorderRight(String borderRight) {
stylize("border-Right", borderRight);
return this;
}
public StyleApplier setBorderTop(String borderTop) {
stylize("border-Top", borderTop);
return this;
}
public StyleApplier setBorderBottom(String borderBottom) {
stylize("border-Bottom", borderBottom);
return this;
}
public StyleApplier setBorderStyle(String borderStyle) {
stylize("border-Style", borderStyle);
return this;
}
public StyleApplier setBorderLeftStyle(String borderLeftStyle) {
stylize("border-Left-Style", borderLeftStyle);
return this;
}
public StyleApplier setBorderRightStyle(String borderRightStyle) {
stylize("border-Right-Style", borderRightStyle);
return this;
}
public StyleApplier setBorderTopStyle(String borderTopStyle) {
stylize("border-Top-Style", borderTopStyle);
return this;
}
public StyleApplier setBorderBottomStyle(String borderBottomStyle) {
stylize("border-Bottom-Style", borderBottomStyle);
return this;
}
public StyleApplier setBorderWidth(String borderWidth) {
stylize("border-Width", borderWidth);
return this;
}
public StyleApplier setBorderLeftWidth(String borderLeftWidth) {
stylize("border-Left-Width", borderLeftWidth);
return this;
}
public StyleApplier setBorderRightWidth(String borderRightWidth) {
stylize("border-Right-Width", borderRightWidth);
return this;
}
public StyleApplier setBorderTopWidth(String borderTopWidth) {
stylize("border-Top-Width", borderTopWidth);
return this;
}
public StyleApplier setBorderBottomWidth(String borderBottomWidth) {
stylize("border-Bottom-Width", borderBottomWidth);
return this;
}
public StyleApplier setBorderColor(String borderColor) {
stylize("border-Color", borderColor);
return this;
}
public StyleApplier setBorderLeftColor(String borderLeftColor) {
stylize("border-Left-Color", borderLeftColor);
return this;
}
public StyleApplier setBorderRightColor(String borderRightColor) {
stylize("border-Right-Color", borderRightColor);
return this;
}
public StyleApplier setBorderTopColor(String borderTopColor) {
stylize("border-Top-Color", borderTopColor);
return this;
}
public StyleApplier setBorderBottomColor(String borderBottomColor) {
stylize("border-Bottom-Color", borderBottomColor);
return this;
}
}
public class ApplyPendingAttribute implements AttributeApplier {
@Override
public void addAttribute(String name, String value) {
AttributeBuilder attr = attributes.get(name);
if (attr == null) {
setAttribute(name, value);
} else {
concat(attr, value);
}
}
protected void concat(AttributeBuilder attr, String value) {
attr.addChild(attr.wrapChars(value));
}
@Override
public void setAttribute(String name, String value) {
if ("id".equalsIgnoreCase(name)) {
id = value;
}
attributes.put(name, newAttributeBuilder(value));
}
@Override
public String getAttribute(String name) {
AttributeBuilder attr = attributes.get(name);
if (attr == null) {
return EMPTY;
}
return attr.getElement();
}
@Override
public void removeAttribute(String name) {
attributes.remove(name);
}
}
protected Provider<AttributeApplier> attributeApplier;
protected Provider<StyleApplier> stylizer;
protected final StringTo<AttributeBuilder> attributes;
public ElementBuilder() {
this(false);
}
public ElementBuilder(boolean searchableChildren) {
super(searchableChildren);
attributes = X_Collect.newStringMap(AttributeBuilder.class);
attributeApplier = new LazyProvider<>(new Provider<AttributeApplier>() {
@Override
public AttributeApplier get() {
return createAttributeApplier();
}
});
stylizer = new LazyProvider<>(new Provider<StyleApplier>() {
@Override
public StyleApplier get() {
return createStyleApplier();
}
});
}
@Override
public void cleanup() {
super.cleanup();
attributeApplier = null;
stylizer = null;
attributes.clear();
}
protected abstract StyleApplier createStyleApplier();
protected AttributeApplier createAttributeApplier() {
return new ApplyPendingAttribute();
}
public ElementBuilder<E> setAttribute(String name, String value) {
if ("style".equalsIgnoreCase(name)) {
getStyle().setValue(value);
} else {
if ("id".equalsIgnoreCase(name)) {
id = value;
}
attributeApplier.get().setAttribute(name, value);
}
return this;
}
public ElementBuilder<E> setDataAttribute(String name, String value) {
return setAttribute("data-"+ withPrefix(name), value);
}
public ElementBuilder<E> setClass(String value) {
setAttribute("class", value);
return this;
}
public ElementBuilder<E> addAttribute(String name, String value) {
switch(name.toLowerCase()) {
case "style":
getStyle().addValue(value);
break;
case "id":
id = value;
attributeApplier.get().addAttribute(name, value);
return this;
case "class":
AttributeBuilder was = attributes.get(name);
if (was == null) {
attributes.put(name, newClassnameBuilder());
}
default:
attributeApplier.get().addAttribute(name, value);
}
return this;
}
public ElementBuilder<E> addDataAttribute(String name, String value) {
return addAttribute("data-"+ withPrefix(name), value);
}
public String withPrefix(String dataKey) {
return prefix()+dataKey;
}
protected String prefix() {
return getDefaultPrefix(); // == System.getProperty("data.attr.prefix", "xapi-");
}
protected AttributeBuilder newClassnameBuilder() {
return new ClassnameBuilder();
}
public ElementBuilder<E> removeAttribute(String name) {
attributeApplier.get().removeAttribute(name);
return this;
}
public ElementBuilder<E> setStyle(String name, String value) {
getStyle().applyStyle(this, name, value);
return this;
}
public StyleApplier getStyle() {
return stylizer.get();
}
public ElementBuilder<E> setStyle(String value) {
setAttribute("style", value);
return this;
}
public ElementBuilder<E> removeStyle(String name) {
getStyle().applyStyle(this, name, null);
return this;
}
public ElementBuilder<E> setId(String id) {
this.id = id;
setAttribute("id", id);
return this;
}
public ElementBuilder<E> setSrc(String src) {
setAttribute("src", src);
return this;
}
public ElementBuilder<E> setHref(String href) {
setAttribute("href", href);
return this;
}
public ElementBuilder<E> setTitle(String title) {
setAttribute("title", title);
return this;
}
public ElementBuilder<E> setValue(String value) {
setAttribute("value", value);
return this;
}
public ElementBuilder<E> setChecked(String checked) {
setAttribute("checked", checked);
return this;
}
}