| /* |
| * Copyright 2003-2007 the original author or authors. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package groovy.util.slurpersupport; |
| |
| import groovy.lang.Buildable; |
| import groovy.lang.Closure; |
| import groovy.lang.DelegatingMetaClass; |
| import groovy.lang.GString; |
| import groovy.lang.GroovyObject; |
| import groovy.lang.GroovyObjectSupport; |
| import groovy.lang.GroovyRuntimeException; |
| import groovy.lang.IntRange; |
| import groovy.lang.MetaClass; |
| import groovy.lang.Writable; |
| |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.net.MalformedURLException; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Stack; |
| |
| import org.codehaus.groovy.runtime.DefaultGroovyMethods; |
| |
| |
| /** |
| * @author John Wilson |
| */ |
| |
| public abstract class GPathResult extends GroovyObjectSupport implements Writable, Buildable { |
| protected final GPathResult parent; |
| protected final String name; |
| protected final String namespacePrefix; |
| protected final Map namespaceMap = new HashMap(); |
| protected final Map namespaceTagHints; |
| |
| /** |
| * @param parent |
| * @param name |
| * @param namespacePrefix |
| * @param namespaceTagHints |
| */ |
| public GPathResult(final GPathResult parent, final String name, final String namespacePrefix, final Map namespaceTagHints) { |
| if (parent == null) { |
| // we are the top of the tree |
| this.parent = this; |
| this.namespaceMap.put("xml", "http://www.w3.org/XML/1998/namespace"); // The XML namespace is always defined |
| } else { |
| this.parent = parent; |
| this.namespaceMap.putAll(parent.namespaceMap); |
| } |
| this.name = name; |
| this.namespacePrefix = namespacePrefix; |
| this.namespaceTagHints = namespaceTagHints; |
| |
| setMetaClass(getMetaClass()); // wrap the standard MetaClass with the delegate |
| } |
| |
| /* (non-Javadoc) |
| * @see groovy.lang.GroovyObjectSupport#setMetaClass(groovy.lang.MetaClass) |
| */ |
| public void setMetaClass(final MetaClass metaClass) { |
| final MetaClass newMetaClass = new DelegatingMetaClass(metaClass) { |
| /* (non-Javadoc) |
| * @see groovy.lang.DelegatingMetaClass#getAttribute(java.lang.Object, java.lang.String) |
| */ |
| public Object getAttribute(final Object object, final String attribute) { |
| return GPathResult.this.getProperty("@" + attribute); |
| } |
| |
| public void setAttribute(final Object object, final String attribute, final Object newValue) { |
| GPathResult.this.setProperty("@" + attribute, newValue); |
| } |
| }; |
| super.setMetaClass(newMetaClass); |
| } |
| |
| public Object getProperty(final String property) { |
| if ("..".equals(property)) { |
| return parent(); |
| } else if ("*".equals(property)) { |
| return children(); |
| } else if ("**".equals(property)) { |
| return depthFirst(); |
| } else if (property.startsWith("@")) { |
| if (property.indexOf(":") != -1) { |
| final int i = property.indexOf(":"); |
| return new Attributes(this, "@" + property.substring(i + 1), property.substring(1, i), this.namespaceTagHints); |
| } else { |
| return new Attributes(this, property, this.namespaceTagHints); |
| } |
| } else { |
| if (property.indexOf(":") != -1) { |
| final int i = property.indexOf(":"); |
| return new NodeChildren(this, property.substring(i + 1), property.substring(0, i), this.namespaceTagHints); |
| } else { |
| return new NodeChildren(this, property, this.namespaceTagHints); |
| } |
| } |
| } |
| |
| public void setProperty(final String property, final Object newValue) { |
| if (property.startsWith("@")) { |
| if (newValue instanceof String || newValue instanceof GString) { |
| final Iterator iter = iterator(); |
| |
| while (iter.hasNext()) { |
| final NodeChild child = (NodeChild)iter.next(); |
| |
| child.attributes().put(property.substring(1), newValue); |
| } |
| } |
| } else { |
| final GPathResult result = new NodeChildren(this, property, this.namespaceTagHints); |
| |
| if (newValue instanceof Map) { |
| final Iterator iter = ((Map)newValue).entrySet().iterator(); |
| |
| while (iter.hasNext()) { |
| final Map.Entry entry = (Map.Entry)iter.next(); |
| |
| result.setProperty("@" + entry.getKey(), entry.getValue()); |
| } |
| } else { |
| if (newValue instanceof Closure) { |
| result.replaceNode((Closure)newValue); |
| } else { |
| result.replaceBody(newValue); |
| } |
| } |
| } |
| } |
| |
| public Object leftShift(final Object newValue) { |
| appendNode(newValue); |
| return this; |
| } |
| |
| public Object plus(final Object newValue) { |
| this.replaceNode(new Closure(this) { |
| public void doCall(Object[] args) { |
| final GroovyObject delegate = (GroovyObject)getDelegate(); |
| |
| delegate.getProperty("mkp"); |
| delegate.invokeMethod("yield", args); |
| |
| delegate.getProperty("mkp"); |
| delegate.invokeMethod("yield", new Object[]{newValue}); |
| } |
| }); |
| |
| return this; |
| } |
| |
| protected abstract void replaceNode(Closure newValue); |
| |
| protected abstract void replaceBody(Object newValue); |
| |
| protected abstract void appendNode(Object newValue); |
| |
| public String name() { |
| return this.name; |
| } |
| |
| public GPathResult parent() { |
| return this.parent; |
| } |
| |
| public GPathResult children() { |
| return new NodeChildren(this, this.namespaceTagHints); |
| } |
| |
| public String lookupNamespace(final String prefix) { |
| return (String)this.namespaceTagHints.get(prefix); |
| } |
| |
| public String toString() { |
| return text(); |
| } |
| |
| public Integer toInteger() { |
| return DefaultGroovyMethods.toInteger(text()); |
| } |
| |
| public Long toLong() { |
| return DefaultGroovyMethods.toLong(text()); |
| } |
| |
| public Float toFloat() { |
| return DefaultGroovyMethods.toFloat(text()); |
| } |
| |
| public Double toDouble() { |
| return DefaultGroovyMethods.toDouble(text()); |
| } |
| |
| public BigDecimal toBigDecimal() { |
| return DefaultGroovyMethods.toBigDecimal(text()); |
| } |
| |
| public BigInteger toBigInteger() { |
| return DefaultGroovyMethods.toBigInteger(text()); |
| } |
| |
| public URL toURL() throws MalformedURLException { |
| return DefaultGroovyMethods.toURL(text()); |
| } |
| |
| public URI toURI() throws URISyntaxException { |
| return DefaultGroovyMethods.toURI(text()); |
| } |
| |
| public Boolean toBoolean() { |
| return DefaultGroovyMethods.toBoolean(text()); |
| } |
| |
| public GPathResult declareNamespace(final Map newNamespaceMapping) { |
| this.namespaceMap.putAll(newNamespaceMapping); |
| return this; |
| } |
| |
| /* (non-Javadoc) |
| * @see java.lang.Object#equals(java.lang.Object) |
| */ |
| public boolean equals(Object obj) { |
| return text().equals(obj.toString()); |
| } |
| |
| public Object getAt(final int index) { |
| if (index < 0) throw new ArrayIndexOutOfBoundsException(index); |
| |
| final Iterator iter = iterator(); |
| int count = 0; |
| |
| while (iter.hasNext()) { |
| if (count++ == index) { |
| return iter.next(); |
| } else { |
| iter.next(); |
| } |
| } |
| |
| return new NoChildren(this, this.name, this.namespaceTagHints); |
| } |
| |
| public Object getAt(final IntRange range) { |
| final int from = range.getFromInt(); |
| final int to = range.getToInt(); |
| |
| if (range.isReverse()) { |
| throw new GroovyRuntimeException("Reverse ranges not supported, range supplied is ["+ to + ".." + from + "]"); |
| } else if (from < 0 || to < 0) { |
| throw new GroovyRuntimeException("Negative range indexes not supported, range supplied is ["+ from + ".." + to + "]"); |
| } else { |
| return new Iterator() { |
| final Iterator iter = iterator(); |
| Object next; |
| int count = 0; |
| |
| public boolean hasNext() { |
| if (count <= to) { |
| while (iter.hasNext()) { |
| if (count++ >= from) { |
| this.next = iter.next(); |
| return true; |
| } else { |
| iter.next(); |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| public Object next() { |
| return next; |
| } |
| |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| }; |
| } |
| } |
| |
| public void putAt(final int index, final Object newValue) { |
| final GPathResult result = (GPathResult)getAt(index); |
| |
| if (newValue instanceof Closure) { |
| result.replaceNode((Closure)newValue); |
| } else { |
| result.replaceBody(newValue); |
| } |
| } |
| |
| public Iterator depthFirst() { |
| return new Iterator() { |
| private final List list = new LinkedList(); |
| private final Stack stack = new Stack(); |
| private Iterator iter = iterator(); |
| private GPathResult next = getNextByDepth(); |
| |
| public boolean hasNext() { |
| return this.next != null; |
| } |
| |
| public Object next() { |
| try { |
| return this.next; |
| } finally { |
| this.next = getNextByDepth(); |
| } |
| } |
| |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| private GPathResult getNextByDepth() { |
| while (this.iter.hasNext()) { |
| final GPathResult node = (GPathResult) this.iter.next(); |
| this.list.add(node); |
| this.stack.push(this.iter); |
| this.iter = node.children().iterator(); |
| } |
| |
| if (this.list.isEmpty()) { |
| return null; |
| } else { |
| GPathResult result = (GPathResult) this.list.get(0); |
| this.list.remove(0); |
| this.iter = (Iterator) this.stack.pop(); |
| return result; |
| } |
| } |
| }; |
| } |
| |
| /** |
| * An iterator useful for traversing XML documents/fragments in breadth-first order. |
| * |
| * @return Iterator the iterator of GPathResult objects |
| */ |
| public Iterator breadthFirst() { |
| return new Iterator() { |
| private final List list = new LinkedList(); |
| private Iterator iter = iterator(); |
| private GPathResult next = getNextByBreadth(); |
| |
| public boolean hasNext() { |
| return this.next != null; |
| } |
| |
| public Object next() { |
| try { |
| return this.next; |
| } finally { |
| this.next = getNextByBreadth(); |
| } |
| } |
| |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| private GPathResult getNextByBreadth() { |
| List children = new ArrayList(); |
| while (this.iter.hasNext() || !children.isEmpty()) { |
| if (this.iter.hasNext()) { |
| final GPathResult node = (GPathResult) this.iter.next(); |
| this.list.add(node); |
| this.list.add(this.iter); |
| children.add(node.children()); |
| } else { |
| List nextLevel = new ArrayList(); |
| for (int i = 0; i < children.size(); i++) { |
| GPathResult next = (GPathResult) children.get(i); |
| Iterator iterator = next.iterator(); |
| while (iterator.hasNext()) { |
| nextLevel.add(iterator.next()); |
| } |
| } |
| this.iter = nextLevel.iterator(); |
| children = new ArrayList(); |
| } |
| } |
| if (this.list.isEmpty()) { |
| return null; |
| } else { |
| GPathResult result = (GPathResult) this.list.get(0); |
| this.list.remove(0); |
| this.iter = (Iterator) this.list.get(0); |
| this.list.remove(0); |
| return result; |
| } |
| } |
| }; |
| } |
| |
| public List list() { |
| final Iterator iter = nodeIterator(); |
| final List result = new LinkedList(); |
| while (iter.hasNext()) { |
| result.add(new NodeChild((Node) iter.next(), this.parent, this.namespacePrefix, this.namespaceTagHints)); |
| } |
| return result; |
| } |
| |
| public boolean isEmpty() { |
| return size() == 0; |
| } |
| |
| public Closure getBody() { |
| return new Closure(this.parent(),this) { |
| public void doCall(Object[] args) { |
| final GroovyObject delegate = (GroovyObject)getDelegate(); |
| final GPathResult thisObject = (GPathResult)getThisObject(); |
| |
| Node node = (Node)thisObject.getAt(0); |
| List children = node.children(); |
| |
| for(int i=0; i<children.size(); i++){ |
| Object child = children.get(i); |
| delegate.getProperty("mkp"); |
| if(child instanceof Node){ |
| delegate.invokeMethod("yield", new Object[]{new NodeChild((Node)child, thisObject,"*",null)}); |
| } |
| else{ |
| delegate.invokeMethod("yield", new Object[]{child}); |
| } |
| } |
| } |
| }; |
| } |
| |
| public abstract int size(); |
| |
| public abstract String text(); |
| |
| public abstract GPathResult parents(); |
| |
| public abstract Iterator childNodes(); |
| |
| public abstract Iterator iterator(); |
| |
| public abstract GPathResult find(Closure closure); |
| |
| public abstract GPathResult findAll(Closure closure); |
| |
| public abstract Iterator nodeIterator(); |
| } |