package org.nutz.lang.segment;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.nutz.lang.Lang;
import org.nutz.lang.Mirror;
import org.nutz.lang.util.Context;
import org.nutz.lang.util.NutMap;
public class CharSegment implements Segment, Cloneable {
public CharSegment() {}
public CharSegment(String str) {
valueOf(str);
}
@SuppressWarnings("unchecked")
public Segment add(String key, Object v) {
if (!context.has(key)) {
context.set(key, v);
return this;
}
Object val = context.get(key);
if (val == null) {
context.set(key, v);
} else if (val instanceof Collection<?>) {
((Collection<Object>) val).add(v);
} else {
List<Object> objSet = new LinkedList<Object>();
objSet.add(val);
objSet.add(v);
context.set(key, objSet);
}
return this;
}
public void clearAll() {
context.clear();
}
public boolean contains(String key) {
return keys.containsKey(key);
}
public Segment born() {
return new CharSegment(this.getOrginalString());
}
private String orgString;
public String getOrginalString() {
return orgString;
}
public Segment clone() {
CharSegment cs = new CharSegment();
cs.parse(Lang.inr(orgString));
cs.context = this.context.clone();
return cs;
}
public Set<String> keys() {
return this.keys.keySet();
}
public int keyCount() {
return this.keys.size();
}
public boolean hasKey() {
return !this.keys.isEmpty();
}
public List<Object> values() {
List<Object> re = new ArrayList<Object>(nodes.size());
for (SegmentNode node : nodes) {
if (node.isKey())
re.add(context.get(node.getValue()));
else
re.add(node.getValue());
}
return re;
}
public Segment setAll(Object v) {
for (String key : keys())
context.set(key, v);
return this;
}
public Segment setBy(Object obj) {
Iterator<String> it = keys().iterator();
Class<?> klass = obj.getClass();
Mirror<?> mirror = Mirror.me(klass);
// Primitive Type: set it to all PlugPoints
if (mirror.isStringLike() || mirror.isBoolean() || mirror.isNumber() || mirror.isChar()) {
this.setAll(obj);
}
// Map: set by key
else if (mirror.isOf(Map.class)) {
Map<?, ?> map = (Map<?, ?>) obj;
while (it.hasNext()) {
String key = it.next();
try {
this.set(key, map.get(key));
}
catch (Exception e) {
this.set(key, "");
}
}
}
// POJO: set by field
else {
while (it.hasNext()) {
String key = it.next();
try {
this.set(key, mirror.getValue(obj, key));
}
catch (Exception e) {
this.set(key, "");
}
}
}
return this;
}
public Segment set(String key, Object v) {
context.set(key, v);
return this;
}
public List<SegmentNode> getNodes() {
return nodes;
}
private Context context;
private List<SegmentNode> nodes;
private NutMap keys;
public void parse(Reader reader) {
nodes = new LinkedList<SegmentNode>();
context = Lang.context();
keys = new NutMap();
StringBuilder org = new StringBuilder();
StringBuilder sb = new StringBuilder();
int b;
try {
while (-1 != (b = reader.read())) {
org.append((char) b);
switch (b) {
case '$':
b = reader.read();
org.append((char) b);
// Escape
if (b == '$') {
sb.append((char) b);
}
// In Plug Point
else if (b == '{') {
// Save before
if (sb.length() > 0) {
nodes.add(SegmentNode.val(sb.toString()));
sb = new StringBuilder();
}
// Search the end
while (-1 != (b = reader.read())) {
org.append((char) b);
if (b == '}')
break;
sb.append((char) b);
}
if (b != '}')
throw Lang.makeThrow("Error format around '%s'", sb);
// Create Key
String key = sb.toString();
nodes.add(SegmentNode.key(key));
keys.put(key, null);
sb = new StringBuilder();
}
// Normal
else {
sb.append('$').append((char) b);
}
break;
default:
sb.append((char) b);
}
}
if (sb.length() > 0)
nodes.add(SegmentNode.val(sb.toString()));
// Store the Oraginal Value
orgString = org.toString();
}
catch (IOException e) {
throw Lang.wrapThrow(e);
}
}
public Segment valueOf(String str) {
parse(new StringReader(str));
return this;
}
public CharSequence render() {
return render(context);
}
public CharSequence render(Context context) {
StringBuilder sb = new StringBuilder();
for (SegmentNode node : nodes) {
Object val = node.isKey() ? context.get(node.getValue()) : node.getValue();
if (null == val)
continue;
if (val instanceof Collection<?>) {
for (Object obj : (Collection<?>) val) {
sb.append(obj);
}
} else {
sb.append(val);
}
}
return sb;
}
public Context getContext() {
return context;
}
public void fillNulls(Context context) {
for (String key : keys.keySet()) {
Object val = context.get(key);
if (null == val)
context.set(key, "${" + key + "}");
}
}
public String toString() {
return render().toString();
}
}