1 package groovy.xml.streamingmarkupsupport;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 import groovy.lang.Closure;
48 import groovy.lang.GroovyInterceptable;
49 import groovy.lang.GroovyObjectSupport;
50
51 import java.util.Collections;
52 import java.util.HashMap;
53 import java.util.Map;
54
55 public class BaseMarkupBuilder extends Builder {
56 public BaseMarkupBuilder(final Map namespaceMethodMap) {
57 super(namespaceMethodMap);
58 }
59
60 public Object bind(final Closure root) {
61 return new Document(root, this.namespaceMethodMap);
62 }
63
64 private static class Document extends Built implements GroovyInterceptable {
65 private Object out;
66 private final Map pendingNamespaces = new HashMap();
67 private final Map namespaces = new HashMap();
68 private final Map specialProperties = new HashMap();
69 private String prefix = "";
70
71 {
72
73 namespaces.put("xml", "http://www.w3.org/XML/1998/namespace"); // built in namespace
74 namespaces.put("mkp", "http://www.codehaus.org/Groovy/markup/keywords"); // pseudo namespace for markup keywords
75
76 specialProperties.put("out", new OutputSink("out") {
77 public Object leftShift(final Object value) {
78 return leftShift("yield", value);
79 }
80 });
81 specialProperties.put("unescaped", new OutputSink("unescaped") {
82 public Object leftShift(final Object value) {
83 return leftShift("yieldUnescaped", value);
84 }
85 });
86 specialProperties.put("namespaces", new OutputSink("namespaces") {
87 public Object leftShift(final Object value) {
88 return leftShift("declareNamespace", value);
89 }
90 });
91 specialProperties.put("pi", new OutputSink("pi") {
92 public Object leftShift(final Object value) {
93 return leftShift("pi", value);
94 }
95 });
96 specialProperties.put("comment", new OutputSink("comment") {
97 public Object leftShift(final Object value) {
98 return leftShift("comment", value);
99 }
100 });
101 }
102
103 private abstract class OutputSink extends GroovyObjectSupport {
104 private final String name;
105
106 public OutputSink(final String name) {
107 this.name = name;
108 }
109
110 public Object invokeMethod(final String name, final Object args) {
111 Document.this.prefix = this.name;
112 return Document.this.invokeMethod(name, args);
113 }
114
115 public abstract Object leftShift(Object item);
116
117 protected Object leftShift(final String command, final Object value) {
118 Document.this.getProperty("mkp");
119 Document.this.invokeMethod(command, new Object[]{value});
120 return this;
121 }
122 }
123
124 public Document(final Closure root, final Map namespaceMethodMap) {
125 super(root, namespaceMethodMap);
126 }
127
128
129
130
131 public Object invokeMethod(final String name, final Object args) {
132 final Object[] arguments = (Object[]) args;
133 Map attrs = Collections.EMPTY_MAP;
134 Object body = null;
135
136
137
138
139
140 for (int i = 0; i != arguments.length; i++) {
141 final Object arg = arguments[i];
142
143 if (arg instanceof Map) {
144 attrs = (Map)arg;
145 } else if (arg instanceof Closure) {
146 final Closure c = ((Closure) arg);
147
148 c.setDelegate(this);
149 body = c.asWritable();
150 } else {
151 body = arg;
152 }
153 }
154
155
156
157
158 final Object uri;
159
160 if (this.pendingNamespaces.containsKey(this.prefix)) {
161 uri = this.pendingNamespaces.get(this.prefix);
162 } else if (this.namespaces.containsKey(this.prefix)) {
163 uri = this.namespaces.get(this.prefix);
164 } else {
165 uri = ":";
166 }
167
168 final Object[] info = (Object[])this.namespaceSpecificTags.get(uri);
169 final Map tagMap = (Map)info[2];
170 final Closure defaultTagClosure = (Closure)info[0];
171
172 final String prefix = this.prefix;
173 this.prefix = "";
174
175 if (tagMap.containsKey(name)) {
176 return ((Closure)tagMap.get(name)).call(new Object[]{this, this.pendingNamespaces, this.namespaces, this.namespaceSpecificTags, prefix, attrs, body, this.out});
177 } else {
178 return defaultTagClosure.call(new Object[]{name, this, this.pendingNamespaces, this.namespaces, this.namespaceSpecificTags, prefix, attrs, body, this.out});
179 }
180 }
181
182
183
184
185 public Object getProperty(final String property) {
186 final Object special = this.specialProperties.get(property);
187
188 if (special == null) {
189 this.prefix = property;
190 return this;
191 } else {
192 return special;
193 }
194 }
195
196
197
198
199 public void setProperty(String property, Object newValue) {
200 if ("trigger".equals(property)) {
201 this.out = newValue;
202 this.root.call(this);
203 } else {
204 super.setProperty(property, newValue);
205 }
206 }
207 }
208 }