View Javadoc

    1   /*
    2    * Copyright 2005 John G. Wilson
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17
    18  package groovy.util.slurpersupport;
    19
    20  import groovy.lang.Buildable;
    21  import groovy.lang.Closure;
    22  import groovy.lang.GroovyObject;
    23  import groovy.lang.Writable;
    24
    25  import java.io.IOException;
    26  import java.io.Writer;
    27  import java.util.HashMap;
    28  import java.util.Iterator;
    29  import java.util.LinkedList;
    30  import java.util.List;
    31  import java.util.Map;
    32
    33
    34  /***
    35   * @author John Wilson
    36   *
    37   */
    38
    39  public class Node implements Writable {
    40    private final String name;
    41    private final Map attributes;
    42    private final Map attributeNamespaces;
    43    private final String namespaceURI;
    44    private List children = new LinkedList();
    45
    46    public Node(final Node parent, final String name, final Map attributes, final Map attributeNamespaces, final String namespaceURI) {
    47      this.name = name;
    48      this.attributes = attributes;
    49      this.attributeNamespaces = attributeNamespaces;
    50      this.namespaceURI = namespaceURI;
    51    }
    52
    53    public String name() {
    54      return this.name;
    55    }
    56
    57    public String namespaceURI() {
    58      return this.namespaceURI;
    59    }
    60
    61    public Map attributes() {
    62      return this.attributes;
    63    }
    64
    65    public List children() {
    66      return this.children;
    67    }
    68
    69    public void addChild(final Object child) {
    70      this.children.add(child);
    71    }
    72
    73    /* (non-Javadoc)
    74     * @see org.codehaus.groovy.sandbox.util.slurpersupport.Node#text()
    75     */
    76    public String text() {
    77    final StringBuffer buff = new StringBuffer();
    78    final Iterator iter = this.children.iterator();
    79
    80      while (iter.hasNext()) {
    81      final Object child = iter.next();
    82
    83          if (child instanceof Node) {
    84              buff.append(((Node)child).text());
    85          } else {
    86              buff.append(child);
    87          }
    88      }
    89
    90      return buff.toString();
    91    }
    92
    93    /* (non-Javadoc)
    94     * @see org.codehaus.groovy.sandbox.util.slurpersupport.Node#childNodes()
    95     */
    96
    97    public Iterator childNodes() {
    98      return new Iterator() {
    99        private final Iterator iter = Node.this.children.iterator();
    100       private Object nextElementNodes = getNextElementNodes();
    101
    102       public boolean hasNext() {
    103         return this.nextElementNodes != null;
    104       }
    105
    106       public Object next() {
    107         try {
    108           return this.nextElementNodes;
    109         } finally {
    110           this.nextElementNodes = getNextElementNodes();
    111         }
    112       }
    113
    114       public void remove() {
    115         throw new UnsupportedOperationException();
    116       }
    117
    118       private Object getNextElementNodes() {
    119         while (iter.hasNext()) {
    120         final Object node = iter.next();
    121
    122           if (node instanceof Node) {
    123             return node;
    124           }
    125         }
    126
    127         return null;
    128       }
    129     };
    130   }
    131
    132   /* (non-Javadoc)
    133    * @see org.codehaus.groovy.sandbox.util.slurpersupport.Node#writeTo(java.io.Writer)
    134    */
    135   public Writer writeTo(final Writer out) throws IOException {
    136   final Iterator iter = this.children.iterator();
    137
    138     while (iter.hasNext()) {
    139     final Object child = iter.next();
    140
    141       if (child instanceof Writable) {
    142         ((Writable)child).writeTo(out);
    143       } else {
    144         out.write(child.toString());
    145       }
    146     }
    147
    148     return out;
    149   }
    150
    151   public void build(final GroovyObject builder, final Map namespaceMap, final Map namespaceTagHints) {
    152   final Closure rest = new Closure(null) {
    153                           public Object doCall(final Object o) {
    154                             buildChildren(builder, namespaceMap, namespaceTagHints);
    155
    156                             return null;
    157                           }
    158                         };
    159
    160     if (this.namespaceURI.length() == 0 && this.attributeNamespaces.isEmpty()) {
    161       builder.invokeMethod(this.name, new Object[]{this.attributes, rest});
    162     } else {
    163       final List newTags = new LinkedList();
    164       builder.getProperty("mkp");
    165       final List namespaces = (List)builder.invokeMethod("getNamespaces", new Object[]{});
    166
    167       final Map current = (Map)namespaces.get(0);
    168       final Map pending = (Map)namespaces.get(1);
    169
    170       if (this.attributeNamespaces.isEmpty()) {
    171         builder.getProperty(getTagFor(this.namespaceURI, current, pending, namespaceMap, namespaceTagHints, newTags, builder));
    172         builder.invokeMethod(this.name, new Object[]{this.attributes, rest});
    173       } else {
    174       final Map attributesWithNamespaces = new HashMap(this.attributes);
    175       final Iterator attrs = this.attributes.keySet().iterator();
    176
    177         while (attrs.hasNext()) {
    178         final Object key = attrs.next();
    179         final Object attributeNamespaceURI = this.attributeNamespaces.get(key);
    180
    181           if (attributeNamespaceURI != null) {
    182             attributesWithNamespaces.put(getTagFor(attributeNamespaceURI, current, pending, namespaceMap, namespaceTagHints, newTags, builder) +
    183                                          "$" + key, attributesWithNamespaces.remove(key));
    184           }
    185         }
    186
    187         builder.getProperty(getTagFor(this.namespaceURI, current, pending, namespaceMap,namespaceTagHints,  newTags, builder));
    188         builder.invokeMethod(this.name, new Object[]{attributesWithNamespaces, rest});
    189       }
    190
    191       // remove the new tags we had to define for this element
    192       if (!newTags.isEmpty()) {
    193       final Iterator iter = newTags.iterator();
    194
    195         do {
    196           pending.remove(iter.next());
    197         } while (iter.hasNext());
    198       }
    199
    200     }
    201   }
    202
    203   private static String getTagFor(final Object namespaceURI, final Map current,
    204                                   final Map pending, final Map local, final Map tagHints,
    205                                   final List newTags, final GroovyObject builder) {
    206   String tag = findNamespaceTag(pending, namespaceURI); // look in the namespaces whose declaration has already been emitted
    207
    208     if (tag == null) {
    209       tag = findNamespaceTag(current, namespaceURI);  // look in the namespaces who will be declared at the next element
    210
    211       if (tag == null) {
    212         // we have to declare the namespace - choose a tag
    213         tag = findNamespaceTag(local, namespaceURI);  // If the namespace has been decared in the GPath expression use that tag
    214
    215         if (tag == null || tag.length() == 0) {
    216           tag = findNamespaceTag(tagHints, namespaceURI);  // If the namespace has been used in the parse documant use that tag
    217         }
    218
    219         if (tag == null || tag.length() == 0) { // otherwise make up a new tag and check it has not been used before
    220         int suffix = 0;
    221
    222           do {
    223             final String posibleTag = "tag" + suffix++;
    224
    225             if (!pending.containsKey(posibleTag) && !current.containsKey(posibleTag) && !local.containsKey(posibleTag)) {
    226               tag = posibleTag;
    227             }
    228           } while (tag == null);
    229         }
    230
    231         final Map newNamespace = new HashMap();
    232         newNamespace.put(tag, namespaceURI);
    233         builder.getProperty("mkp");
    234         builder.invokeMethod("declareNamespace", new Object[]{newNamespace});
    235         newTags.add(tag);
    236       }
    237     }
    238
    239     return tag;
    240   }
    241
    242   private static String findNamespaceTag(final Map tagMap, final Object namespaceURI) {
    243     if (tagMap.containsValue(namespaceURI)) {
    244     final Iterator entries = tagMap.entrySet().iterator();
    245
    246       while (entries.hasNext()) {
    247         final Map.Entry entry = (Map.Entry)entries.next();
    248
    249         if (namespaceURI.equals(entry.getValue())) {
    250           return (String)entry.getKey();
    251         }
    252       }
    253     }
    254
    255     return null;
    256   }
    257
    258   private void buildChildren(final GroovyObject builder, final Map namespaceMap, final Map namespaceTagHints) {
    259   final Iterator iter = this.children.iterator();
    260
    261     while (iter.hasNext()) {
    262     final Object child = iter.next();
    263
    264       if (child instanceof Node) {
    265         ((Node)child).build(builder, namespaceMap, namespaceTagHints);
    266       } else if (child instanceof Buildable) {
    267         ((Buildable)child).build(builder);
    268       } else {
    269         builder.getProperty("mkp");
    270         builder.invokeMethod("yield", new Object[]{child});
    271       }
    272     }
    273   }
    274 }