/* ************************************************************************
#
# DivConq
#
# http://divconq.com/
#
# Copyright:
# Copyright 2014 eTimeline, LLC. All rights reserved.
#
# License:
# See the license.txt file in the project's top-level directory for details.
#
# Authors:
# * Andy White
#
************************************************************************ */
package divconq.web.dcui;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import divconq.util.ArrayUtil;
import divconq.web.WebContext;
import divconq.xml.XNode;
abstract public class Element extends Node {
class PartCollectInfo
{
public Object[] args = null;
public boolean top = false;
}
protected String name = null;
protected Map<String, String> attributes = new HashMap<String, String>();
protected List<Node> children = new ArrayList<Node>();
public Object[] myArguments = null;
public String getName() {
return this.name;
}
public void setName(String value) {
this.name = value;
}
public Map<String, String> getAttributes() {
return this.attributes;
}
public Collection<Node> getChildren() {
return this.children;
}
public String getAttribute(String name) {
return this.attributes.get(name);
}
public boolean hasAttribute(String name) {
return this.attributes.containsKey(name);
}
public void addAttribute(String name, String value) {
this.attributes.put(name, value);
}
public void addArgs(Object... args) {
if ((myArguments == null) || (myArguments.length == 0))
this.myArguments = args;
else
this.myArguments = (Object[]) ArrayUtil.addAll(this.myArguments, args);
}
public Element(Object... args) {
super();
this.myArguments = args;
/* not
if (args.length > 0)
this.build(args);
*/
}
@Override
public void doBuild(WebContext ctx) {
this.build(ctx, this.myArguments);
this.myArguments = null;
}
public void build(WebContext ctx, Object... args) {
PartCollectInfo pci = new PartCollectInfo();
pci.args = args;
pci.top = true;
collectParts(ctx, pci);
}
// changes to this, please also update copyArgs
private void collectParts(WebContext ctx, PartCollectInfo info) {
if ((info == null) || (info.args == null))
return;
for (Object obj : info.args) {
if (obj == null)
continue;
if (obj instanceof CharSequence) {
if (info.top) {
this.name = obj.toString();
}
else {
LiteralText txt = new LiteralText(obj.toString());
txt.setParent(this);
this.children.add(txt);
txt.doBuild(ctx);
}
}
else if (obj instanceof Boolean) {
this.setBlockIndent((Boolean)obj);
}
else if (obj instanceof FutureNodes) {
FuturePlaceholder placeholder = new FuturePlaceholder();
placeholder.setParent(this);
this.children.add(placeholder);
placeholder.incrementFuture();
((FutureNodes)obj).setNotify(ctx, placeholder);
}
else if (obj instanceof Nodes) {
try {
for (Node nn : ((Nodes)obj).getList()) {
nn.setParent(this);
this.children.add(nn);
nn.doBuild(ctx);
}
}
catch (Exception x) {
// TODO tracing - catches issues with FutureNodes
}
}
else if (obj instanceof Object[]) {
PartCollectInfo pci = new PartCollectInfo();
pci.args = (Object[])obj;
pci.top = false;
collectParts(ctx, pci);
}
else if (obj instanceof Attributes) {
Attributes attrs = (Attributes)obj;
while (attrs.hasMore()) {
String aname = attrs.pop();
String avalue = this.expandMacro(ctx, attrs.pop());
if (avalue != null)
this.attributes.put(aname, avalue);
}
}
else if (obj instanceof Node) {
Node nn = (Node)obj;
nn.setParent(this);
this.children.add(nn);
nn.doBuild(ctx);
}
}
}
/*
@Override
public void awaitForFutures(final OperationCallback cb) {
super.awaitForFutures(new OperationCallback() {
@Override
public void callback() {
final AtomicInteger counter = new AtomicInteger(Element.this.children.size());
for (Node node : Element.this.children) {
node.awaitForFutures(new OperationCallback() {
@Override
public void callback() {
int cnt = counter.decrementAndGet();
if (cnt == 0)
cb.callback();
}
});
}
}
});
}
*/
public void writeDynamicChildren(PrintStream buffer, String tabs) {
boolean first = true;
for (Node child : this.children) {
if (child.writeDynamic(buffer, tabs + "\t", first))
first = false;
}
}
@Override
public boolean writeDynamic(PrintStream buffer, String tabs, boolean first) {
if (!first)
buffer.println(",");
buffer.println(tabs + "{");
buffer.print(tabs + "\tElement: '" + this.name + "'");
boolean hasCoreAttrs = false;
boolean missingFinalLine = false;
for (Entry<String, String> entry : this.attributes.entrySet()) {
if (Character.isLowerCase(entry.getKey().charAt(0))) {
hasCoreAttrs = true;
continue;
}
buffer.println(",");
buffer.print(tabs + "\t" + entry.getKey() + ": ");
String v = XNode.unquote(entry.getValue());
if (v == null) {
buffer.print("null");
}
else if (v.startsWith("{") && v.endsWith("}"))
buffer.print(v);
else {
buffer.print("'");
Node.writeDynamicJsString(buffer, v);
buffer.print("'");
}
missingFinalLine = true;
}
if (hasCoreAttrs) {
buffer.println(",");
buffer.println(tabs + "\tAttributes: {");
boolean firstattr = true;
for (Entry<String, String> entry : this.attributes.entrySet()) {
if (Character.isUpperCase(entry.getKey().charAt(0)))
continue;
if (firstattr)
firstattr = false;
else
buffer.println(",");
String v = XNode.unquote(entry.getValue());
if (v == null) {
buffer.print(tabs + "\t\t'" + entry.getKey() + "': null");
}
else {
buffer.print(tabs + "\t\t'" + entry.getKey() + "': '");
Node.writeDynamicJsString(buffer, v);
buffer.print("'");
}
}
buffer.println();
buffer.print(tabs + "\t}");
missingFinalLine = true;
}
if (this.children.size() > 0) {
buffer.println(",");
buffer.println(tabs + "\tChildren: [");
this.writeDynamicChildren(buffer, tabs + "\t\t");
buffer.println();
buffer.print(tabs + "\t]");
missingFinalLine = true;
}
if (missingFinalLine)
buffer.println();
buffer.print(tabs + "}");
return true;
}
@Override
public void stream(WebContext ctx, PrintStream strm, String indent, boolean firstchild, boolean fromblock) {
if (this.name == null)
return;
String newindent = indent;
if (this.getBlockIndent() || firstchild) {
this.print(ctx, strm, indent, false, "<" + this.name);
newindent = indent + " ";
}
else {
this.print(ctx, strm, "", false, "<" + this.name);
}
for (String name : this.attributes.keySet()) {
String ev = this.attributes.get(name);
//this.print(strm, "", false, " " + name + "=\"" + divconq.xml.XNode.quote(ev) + "\"");
this.print(ctx, strm, "", false, " " + name + "=\"" + ev + "\"");
}
if (this.children.size() > 0) {
this.print(ctx, strm, "", this.getBlockIndent(), ">");
boolean fromon = fromblock;
boolean lastblock = false;
boolean firstch = this.getBlockIndent(); // only true once, and only if bi
for (Node node : this.children) {
if (node.getBlockIndent() && !lastblock && !fromon)
this.print(ctx, strm, "", true, "");
node.stream(ctx, strm, newindent, (firstch || lastblock), this.getBlockIndent());
lastblock = node.getBlockIndent();
firstch = false;
fromon = false;
}
if (this.getBlockIndent()) {
if (!lastblock)
this.print(ctx, strm, "", true, "");
this.print(ctx, strm, indent, true, "</" + this.name + "> ");
}
else {
this.print(ctx, strm, "", false, "</" + this.name + "> ");
}
}
else {
if (this.getBlockIndent()) {
this.print(ctx, strm, "", true, "></" + this.name + "> ");
}
else {
this.print(ctx, strm, "", false, "/> ");
}
}
}
}