001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.servicemix.soap.util.stax;
018    
019    /*
020     * This implementation comes from the XFire project
021     * https://svn.codehaus.org/xfire/trunk/xfire/xfire-core/src/main/org/codehaus/xfire/util/stax/
022     */
023    
024    import java.util.ArrayList;
025    import java.util.HashMap;
026    import java.util.Iterator;
027    import java.util.List;
028    import java.util.Map;
029    
030    import javax.xml.namespace.NamespaceContext;
031    import javax.xml.namespace.QName;
032    import javax.xml.stream.Location;
033    import javax.xml.stream.XMLStreamConstants;
034    import javax.xml.stream.XMLStreamException;
035    import javax.xml.stream.XMLStreamReader;
036    
037    import org.w3c.dom.Attr;
038    import org.w3c.dom.CDATASection;
039    import org.w3c.dom.Comment;
040    import org.w3c.dom.Document;
041    import org.w3c.dom.Element;
042    import org.w3c.dom.EntityReference;
043    import org.w3c.dom.NamedNodeMap;
044    import org.w3c.dom.Node;
045    import org.w3c.dom.Text;
046    
047    /**
048     * Abstract logic for creating XMLStreamReader from DOM documents.
049     * Its works using adapters for Element, Node and Attribute ( @see ElementAdapter }
050     * 
051     * @author <a href="mailto:tsztelak@gmail.com">Tomasz Sztelak</a>
052     */
053    public class DOMStreamReader implements XMLStreamReader {
054        public Map properties = new HashMap();
055    
056        private ArrayList<ElementFrame> frames = new ArrayList<ElementFrame>();
057    
058        private ElementFrame frame;
059    
060        private int currentEvent = XMLStreamConstants.START_DOCUMENT;
061    
062        private Node content;
063    
064        private Document document;
065    
066        private DOMNamespaceContext context;
067    
068        /**
069         * @param element
070         */
071        public DOMStreamReader(Element element) {
072            this.frame = new ElementFrame(element, null);
073            frames.add(this.frame);
074            newFrame(frame);
075            this.document = element.getOwnerDocument();
076        }
077    
078        protected ElementFrame getCurrentFrame() {
079            return frame;
080        }
081    
082        /* (non-Javadoc)
083         * @see javax.xml.stream.XMLStreamReader#getProperty(java.lang.String)
084         */
085        public Object getProperty(String key) throws IllegalArgumentException {
086            return properties.get(key);
087        }
088    
089        /* (non-Javadoc)
090         * @see javax.xml.stream.XMLStreamReader#next()
091         */
092        public int next() throws XMLStreamException {
093            if (frame.ended) {
094                frames.remove(frames.size() - 1);
095                if (!frames.isEmpty()) {
096                    frame = (ElementFrame) frames.get(frames.size() - 1);
097                } else {
098                    currentEvent = END_DOCUMENT;
099                    return currentEvent;
100                }
101            }
102    
103            if (!frame.started) {
104                frame.started = true;
105                currentEvent = START_ELEMENT;
106            } else if (frame.currentAttribute < getAttributeCount() - 1) {
107                frame.currentAttribute++;
108                currentEvent = ATTRIBUTE;
109            } else if (frame.currentNamespace < getNamespaceCount() - 1) {
110                frame.currentNamespace++;
111                currentEvent = NAMESPACE;
112            } else if (frame.currentChild < getChildCount() - 1) {
113                frame.currentChild++;
114    
115                currentEvent = moveToChild(frame.currentChild);
116    
117                if (currentEvent == START_ELEMENT) {
118                    ElementFrame newFrame = getChildFrame(frame.currentChild);
119                    newFrame.started = true;
120                    frame = newFrame;
121                    frames.add(this.frame);
122                    currentEvent = START_ELEMENT;
123    
124                    newFrame(newFrame);
125                }
126            } else {
127                frame.ended = true;
128                currentEvent = END_ELEMENT;
129                endElement();
130            }
131            return currentEvent;
132        }
133    
134        protected void skipFrame() {
135            frame.ended = true;
136            currentEvent = END_ELEMENT;
137        }
138    
139        /* (non-Javadoc)
140         * @see javax.xml.stream.XMLStreamReader#require(int, java.lang.String, java.lang.String)
141         */
142        public void require(int arg0, String arg1, String arg2) throws XMLStreamException {
143            throw new UnsupportedOperationException();
144        }
145    
146        /* (non-Javadoc)
147         * @see javax.xml.stream.XMLStreamReader#nextTag()
148         */
149        public int nextTag() throws XMLStreamException {
150            while (hasNext()) {
151                int e = next();
152                if (e == START_ELEMENT || e == END_ELEMENT)
153                    return e;
154            }
155    
156            return currentEvent;
157        }
158    
159        /* (non-Javadoc)
160         * @see javax.xml.stream.XMLStreamReader#hasNext()
161         */
162        public boolean hasNext() throws XMLStreamException {
163            return !(frames.size() == 0 && frame.ended);
164    
165        }
166    
167        /* (non-Javadoc)
168         * @see javax.xml.stream.XMLStreamReader#close()
169         */
170        public void close() throws XMLStreamException {
171        }
172    
173        /* (non-Javadoc)
174         * @see javax.xml.stream.XMLStreamReader#isStartElement()
175         */
176        public boolean isStartElement() {
177            return (currentEvent == START_ELEMENT);
178        }
179    
180        /* (non-Javadoc)
181         * @see javax.xml.stream.XMLStreamReader#isEndElement()
182         */
183        public boolean isEndElement() {
184            return (currentEvent == END_ELEMENT);
185        }
186    
187        /* (non-Javadoc)
188         * @see javax.xml.stream.XMLStreamReader#isCharacters()
189         */
190        public boolean isCharacters() {
191            return (currentEvent == CHARACTERS);
192        }
193    
194        /* (non-Javadoc)
195         * @see javax.xml.stream.XMLStreamReader#isWhiteSpace()
196         */
197        public boolean isWhiteSpace() {
198            return (currentEvent == SPACE);
199        }
200    
201        public int getEventType() {
202            return currentEvent;
203        }
204    
205        public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) throws XMLStreamException {
206            char[] src = getText().toCharArray();
207    
208            if (sourceStart + length >= src.length)
209                length = src.length - sourceStart;
210    
211            for (int i = 0; i < length; i++) {
212                target[targetStart + i] = src[i + sourceStart];
213            }
214    
215            return length;
216        }
217    
218        public boolean hasText() {
219            return (currentEvent == CHARACTERS || currentEvent == DTD || currentEvent == ENTITY_REFERENCE
220                            || currentEvent == COMMENT || currentEvent == SPACE);
221        }
222    
223        public Location getLocation() {
224            return new Location() {
225    
226                public int getCharacterOffset() {
227                    return 0;
228                }
229    
230                public int getColumnNumber() {
231                    return 0;
232                }
233    
234                public int getLineNumber() {
235                    return 0;
236                }
237    
238                public String getPublicId() {
239                    return null;
240                }
241    
242                public String getSystemId() {
243                    return null;
244                }
245    
246            };
247        }
248    
249        public boolean hasName() {
250            return (currentEvent == START_ELEMENT || currentEvent == END_ELEMENT);
251        }
252    
253        public String getVersion() {
254            return null;
255        }
256    
257        public boolean isStandalone() {
258            return false;
259        }
260    
261        public boolean standaloneSet() {
262            // TODO Auto-generated method stub
263            return false;
264        }
265    
266        public String getCharacterEncodingScheme() {
267            // TODO Auto-generated method stub
268            return null;
269        }
270    
271        /**
272         * Get the document associated with this stream.
273         * 
274         * @return
275         */
276        public Document getDocument() {
277            return document;
278        }
279    
280        /**
281         * Find name spaces declaration in attributes and move them to separate
282         * collection.
283         */
284        protected void newFrame(ElementFrame frame) {
285            Element element = getCurrentElement();
286            frame.uris = new ArrayList<String>();
287            frame.prefixes = new ArrayList<String>();
288            frame.attributes = new ArrayList<Node>();
289    
290            if (context == null)
291                context = new DOMNamespaceContext();
292    
293            context.setElement(element);
294    
295            NamedNodeMap nodes = element.getAttributes();
296    
297            String ePrefix = element.getPrefix();
298            if (ePrefix == null) {
299                ePrefix = "";
300            }
301    
302            for (int i = 0; i < nodes.getLength(); i++) {
303                Node node = nodes.item(i);
304                String prefix = node.getPrefix();
305                String localName = node.getLocalName();
306                String value = node.getNodeValue();
307                String name = node.getNodeName();
308    
309                if (prefix == null)
310                    prefix = "";
311    
312                if (name != null && name.equals("xmlns")) {
313                    frame.uris.add(value);
314                    frame.prefixes.add("");
315                } else if (prefix.length() > 0 && prefix.equals("xmlns")) {
316                    frame.uris.add(value);
317                    frame.prefixes.add(localName);
318                } else if (name.startsWith("xmlns:")) {
319                    prefix = name.substring(6);
320                    frame.uris.add(value);
321                    frame.prefixes.add(prefix);
322                } else {
323                    frame.attributes.add(node);
324                }
325            }
326        }
327    
328        protected void endElement() {
329        }
330    
331        protected Element getCurrentElement() {
332            return (Element) getCurrentFrame().element;
333        }
334    
335        public Element skipElement() {
336            Element e = (Element) getCurrentFrame().element;
337            skipFrame();
338            return e;
339        }
340    
341        protected ElementFrame getChildFrame(int currentChild) {
342            return new ElementFrame(getCurrentElement().getChildNodes().item(currentChild), getCurrentFrame());
343        }
344    
345        protected int getChildCount() {
346            return getCurrentElement().getChildNodes().getLength();
347        }
348    
349        protected int moveToChild(int currentChild) {
350            this.content = getCurrentElement().getChildNodes().item(currentChild);
351    
352            if (content instanceof Text)
353                return CHARACTERS;
354            else if (content instanceof Element)
355                return START_ELEMENT;
356            else if (content instanceof CDATASection)
357                return CDATA;
358            else if (content instanceof Comment)
359                return CHARACTERS;
360            else if (content instanceof EntityReference)
361                return ENTITY_REFERENCE;
362    
363            throw new IllegalStateException();
364        }
365    
366        public String getElementText() throws XMLStreamException {
367            return getText();
368        }
369    
370        public String getNamespaceURI(String prefix) {
371            ElementFrame frame = getCurrentFrame();
372    
373            while (null != frame) {
374                int index = frame.prefixes.indexOf(prefix);
375                if (index != -1) {
376                    return (String) frame.uris.get(index);
377                }
378    
379                frame = frame.parent;
380            }
381    
382            return null;
383        }
384    
385        public String getAttributeValue(String ns, String local) {
386            if (ns == null || ns.equals(""))
387                return getCurrentElement().getAttribute(local);
388            else
389                return getCurrentElement().getAttributeNS(ns, local);
390        }
391    
392        public int getAttributeCount() {
393            return getCurrentFrame().attributes.size();
394        }
395    
396        Attr getAttribute(int i) {
397            return (Attr) getCurrentFrame().attributes.get(i);
398        }
399    
400        private String getLocalName(Attr attr) {
401    
402            String name = attr.getLocalName();
403            if (name == null) {
404                name = attr.getNodeName();
405            }
406            return name;
407        }
408    
409        public QName getAttributeName(int i) {
410            Attr at = getAttribute(i);
411    
412            String prefix = at.getPrefix();
413            String ln = getLocalName(at);
414            // at.getNodeName();
415            String ns = at.getNamespaceURI();
416    
417            if (prefix == null) {
418                return new QName(ns, ln);
419            } else {
420                return new QName(ns, ln, prefix);
421            }
422        }
423    
424        public String getAttributeNamespace(int i) {
425            return getAttribute(i).getNamespaceURI();
426        }
427    
428        public String getAttributeLocalName(int i) {
429            Attr attr = getAttribute(i);
430            String name = getLocalName(attr);
431            return name;
432        }
433    
434        public String getAttributePrefix(int i) {
435            return getAttribute(i).getPrefix();
436        }
437    
438        public String getAttributeType(int i) {
439            return toStaxType(getAttribute(i).getNodeType());
440        }
441    
442        public static String toStaxType(short jdom) {
443            switch (jdom) {
444            default:
445                return null;
446            }
447        }
448    
449        public String getAttributeValue(int i) {
450            return getAttribute(i).getValue();
451        }
452    
453        public boolean isAttributeSpecified(int i) {
454            return getAttribute(i).getValue() != null;
455        }
456    
457        public int getNamespaceCount() {
458            return getCurrentFrame().prefixes.size();
459        }
460    
461        public String getNamespacePrefix(int i) {
462            return (String) getCurrentFrame().prefixes.get(i);
463        }
464    
465        public String getNamespaceURI(int i) {
466            return (String) getCurrentFrame().uris.get(i);
467        }
468    
469        public NamespaceContext getNamespaceContext() {
470            return context;
471        }
472    
473        public String getText() {
474            Node node = getCurrentElement().getChildNodes().item(getCurrentFrame().currentChild);
475            return node.getNodeValue();
476        }
477    
478        public char[] getTextCharacters() {
479            return getText().toCharArray();
480        }
481    
482        public int getTextStart() {
483            return 0;
484        }
485    
486        public int getTextLength() {
487            return getText().length();
488        }
489    
490        public String getEncoding() {
491            return null;
492        }
493    
494        public QName getName() {
495            Element el = getCurrentElement();
496    
497            String prefix = getPrefix();
498            String ln = getLocalName();
499    
500            if (prefix == null) {
501                return new QName(el.getNamespaceURI(), ln);
502            } else {
503                return new QName(el.getNamespaceURI(), ln, prefix);
504            }
505        }
506    
507        public String getLocalName() {
508            String name = getCurrentElement().getLocalName();
509            // When the element has no namespaces, null is returned
510            if (name == null) {
511                name = getCurrentElement().getNodeName();
512            }
513            return name;
514        }
515    
516        public String getNamespaceURI() {
517            return getCurrentElement().getNamespaceURI();
518        }
519    
520        public String getPrefix() {
521            String prefix = getCurrentElement().getPrefix();
522            if (prefix == null) {
523                prefix = "";
524            }
525            return prefix;
526        }
527    
528        public String getPITarget() {
529            throw new UnsupportedOperationException();
530        }
531    
532        public String getPIData() {
533            throw new UnsupportedOperationException();
534        }
535    
536        public class DOMNamespaceContext implements NamespaceContext {
537            private Element currentNode;
538    
539            public String getNamespaceURI(String prefix) {
540                String name = prefix;
541                if (name.length() == 0)
542                    name = "xmlns";
543                else
544                    name = "xmlns:" + prefix;
545    
546                return getNamespaceURI(currentNode, name);
547            }
548    
549            private String getNamespaceURI(Element e, String name) {
550                Attr attr = e.getAttributeNode(name);
551                if (attr == null) {
552                    Node n = e.getParentNode();
553                    if (n instanceof Element && n != e) {
554                        return getNamespaceURI((Element) n, name);
555                    }
556                } else {
557                    return attr.getValue();
558                }
559    
560                return null;
561            }
562    
563            public String getPrefix(String uri) {
564                return getPrefix(currentNode, uri);
565            }
566    
567            private String getPrefix(Element e, String uri) {
568                NamedNodeMap attributes = e.getAttributes();
569                for (int i = 0; i < attributes.getLength(); i++) {
570                    Attr a = (Attr) attributes.item(i);
571    
572                    String val = a.getValue();
573                    if (val != null && val.equals(uri)) {
574                        String name = a.getNodeName();
575                        if (name.equals("xmlns"))
576                            return "";
577                        else
578                            return name.substring(6);
579                    }
580                }
581    
582                Node n = e.getParentNode();
583                if (n instanceof Element && n != e) {
584                    return getPrefix((Element) n, uri);
585                }
586    
587                return null;
588            }
589    
590            public Iterator<String> getPrefixes(String uri) {
591                List<String> prefixes = new ArrayList<String>();
592    
593                String prefix = getPrefix(uri);
594                if (prefix != null)
595                    prefixes.add(prefix);
596    
597                return prefixes.iterator();
598            }
599    
600            public Element getElement() {
601                return currentNode;
602            }
603    
604            public void setElement(Element currentNode) {
605                this.currentNode = currentNode;
606            }
607        }
608    
609        public static class ElementFrame {
610            public ElementFrame(Object element, ElementFrame parent) {
611                this.element = element;
612                this.parent = parent;
613            }
614    
615            final Object element;
616            final ElementFrame parent;
617            boolean started = false;
618            boolean ended = false;
619            int currentChild = -1;
620            int currentAttribute = -1;
621            int currentNamespace = -1;
622            int currentElement = -1;
623            List<String> uris;
624            List<String> prefixes;
625            List<Node> attributes;
626            List allAttributes;
627        }
628    
629    }