001: package groovy.xml.streamingmarkupsupport;
002:
003: /*
004:
005: Copyright 2004 (C) John Wilson. All Rights Reserved.
006:
007: Redistribution and use of this software and associated documentation
008: ("Software"), with or without modification, are permitted provided
009: that the following conditions are met:
010:
011: 1. Redistributions of source code must retain copyright
012: statements and notices. Redistributions must also contain a
013: copy of this document.
014:
015: 2. Redistributions in binary form must reproduce the
016: above copyright notice, this list of conditions and the
017: following disclaimer in the documentation and/or other
018: materials provided with the distribution.
019:
020: 3. The name "groovy" must not be used to endorse or promote
021: products derived from this Software without prior written
022: permission of The Codehaus. For written permission,
023: please contact info@codehaus.org.
024:
025: 4. Products derived from this Software may not be called "groovy"
026: nor may "groovy" appear in their names without prior written
027: permission of The Codehaus. "groovy" is a registered
028: trademark of The Codehaus.
029:
030: 5. Due credit should be given to The Codehaus -
031: http://groovy.codehaus.org/
032:
033: THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
034: ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
035: NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
036: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
037: THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
038: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
039: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
040: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
041: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
042: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
043: ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
044: OF THE POSSIBILITY OF SUCH DAMAGE.
045:
046: */
047:
048: import groovy.lang.Closure;
049: import groovy.lang.GroovyInterceptable;
050: import groovy.lang.GroovyObjectSupport;
051:
052: import java.util.Collections;
053: import java.util.HashMap;
054: import java.util.Map;
055:
056: public class BaseMarkupBuilder extends Builder {
057: public BaseMarkupBuilder(final Map namespaceMethodMap) {
058: super (namespaceMethodMap);
059: }
060:
061: public Object bind(final Closure root) {
062: return new Document(root, this .namespaceMethodMap);
063: }
064:
065: private static class Document extends Built implements
066: GroovyInterceptable {
067: private Object out;
068: private final Map pendingNamespaces = new HashMap();
069: private final Map namespaces = new HashMap();
070: private final Map specialProperties = new HashMap();
071: private String prefix = "";
072:
073: {
074:
075: namespaces.put("xml",
076: "http://www.w3.org/XML/1998/namespace"); // built in namespace
077: namespaces.put("mkp",
078: "http://www.codehaus.org/Groovy/markup/keywords"); // pseudo namespace for markup keywords
079:
080: specialProperties.put("out", new OutputSink("out") {
081: public Object leftShift(final Object value) {
082: return leftShift("yield", value);
083: }
084: });
085: specialProperties.put("unescaped", new OutputSink(
086: "unescaped") {
087: public Object leftShift(final Object value) {
088: return leftShift("yieldUnescaped", value);
089: }
090: });
091: specialProperties.put("namespaces", new OutputSink(
092: "namespaces") {
093: public Object leftShift(final Object value) {
094: return leftShift("declareNamespace", value);
095: }
096: });
097: specialProperties.put("pi", new OutputSink("pi") {
098: public Object leftShift(final Object value) {
099: return leftShift("pi", value);
100: }
101: });
102: specialProperties.put("comment", new OutputSink("comment") {
103: public Object leftShift(final Object value) {
104: return leftShift("comment", value);
105: }
106: });
107: }
108:
109: private abstract class OutputSink extends GroovyObjectSupport {
110: private final String name;
111:
112: public OutputSink(final String name) {
113: this .name = name;
114: }
115:
116: public Object invokeMethod(final String name,
117: final Object args) {
118: Document.this .prefix = this .name;
119: return Document.this .invokeMethod(name, args);
120: }
121:
122: public abstract Object leftShift(Object item);
123:
124: protected Object leftShift(final String command,
125: final Object value) {
126: Document.this .getProperty("mkp");
127: Document.this .invokeMethod(command,
128: new Object[] { value });
129: return this ;
130: }
131: }
132:
133: public Document(final Closure root, final Map namespaceMethodMap) {
134: super (root, namespaceMethodMap);
135: }
136:
137: /* (non-Javadoc)
138: * @see groovy.lang.GroovyObject#invokeMethod(java.lang.String, java.lang.Object)
139: */
140: public Object invokeMethod(final String name, final Object args) {
141: final Object[] arguments = (Object[]) args;
142: Map attrs = Collections.EMPTY_MAP;
143: Object body = null;
144:
145: //
146: // Sort the parameters out
147: //
148: for (int i = 0; i != arguments.length; i++) {
149: final Object arg = arguments[i];
150:
151: if (arg instanceof Map) {
152: attrs = (Map) arg;
153: } else if (arg instanceof Closure) {
154: final Closure c = ((Closure) arg);
155:
156: c.setDelegate(this );
157: body = c.asWritable();
158: } else {
159: body = arg;
160: }
161: }
162:
163: //
164: // call the closure corresponding to the tag
165: //
166: final Object uri;
167:
168: if (this .pendingNamespaces.containsKey(this .prefix)) {
169: uri = this .pendingNamespaces.get(this .prefix);
170: } else if (this .namespaces.containsKey(this .prefix)) {
171: uri = this .namespaces.get(this .prefix);
172: } else {
173: uri = ":";
174: }
175:
176: final Object[] info = (Object[]) this .namespaceSpecificTags
177: .get(uri);
178: final Map tagMap = (Map) info[2];
179: final Closure defaultTagClosure = (Closure) info[0];
180:
181: final String prefix = this .prefix;
182: this .prefix = "";
183:
184: if (tagMap.containsKey(name)) {
185: return ((Closure) tagMap.get(name)).call(new Object[] {
186: this , this .pendingNamespaces, this .namespaces,
187: this .namespaceSpecificTags, prefix, attrs,
188: body, this .out });
189: } else {
190: return defaultTagClosure.call(new Object[] { name,
191: this , this .pendingNamespaces, this .namespaces,
192: this .namespaceSpecificTags, prefix, attrs,
193: body, this .out });
194: }
195: }
196:
197: /* (non-Javadoc)
198: * @see groovy.lang.GroovyObject#getProperty(java.lang.String)
199: */
200: public Object getProperty(final String property) {
201: final Object special = this .specialProperties.get(property);
202:
203: if (special == null) {
204: this .prefix = property;
205: return this ;
206: } else {
207: return special;
208: }
209: }
210:
211: /* (non-Javadoc)
212: * @see groovy.lang.GroovyObject#setProperty(java.lang.String, java.lang.Object)
213: */
214: public void setProperty(String property, Object newValue) {
215: if ("trigger".equals(property)) {
216: this.out = newValue;
217: this.root.call(this);
218: } else {
219: super.setProperty(property, newValue);
220: }
221: }
222: }
223: }
|