package com.revolsys.record.io.format.xml; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import javax.xml.stream.XMLStreamConstants; import com.revolsys.collection.iterator.AbstractIterator; import com.revolsys.collection.map.MapEx; import com.revolsys.collection.map.NamedLinkedHashMapEx; import com.revolsys.spring.resource.Resource; import com.revolsys.util.Property; public class XmlMapIterator extends AbstractIterator<MapEx> { /** Flag indicating if there are more records to be read. */ private boolean hasNext = false; private StaxReader in; private Resource resource; private final boolean single; public XmlMapIterator(final Resource resource) { this(resource, false); } public XmlMapIterator(final Resource resource, final boolean single) { this.resource = resource; this.single = single; } @Override protected void closeDo() { super.closeDo(); if (this.in != null) { this.in.close(); this.in = null; } this.resource = null; } @Override protected MapEx getNext() throws NoSuchElementException { if (this.hasNext) { final MapEx map = readMap(); if (this.in.skipToStartElement()) { this.hasNext = false; } return map; } else { throw new NoSuchElementException(); } } @Override protected void initDo() { super.initDo(); this.in = StaxReader.newXmlReader(this.resource); if (this.in.skipToStartElement()) { if (this.single) { this.hasNext = true; } else { if (this.in.skipToStartElement()) { this.hasNext = true; } } } } @SuppressWarnings("unchecked") private Object readElement() { final String name = this.in.getLocalName(); final MapEx map = new NamedLinkedHashMapEx(name); int textIndex = 0; int elementIndex = 0; while (this.in.next() != XMLStreamConstants.END_ELEMENT) { switch (this.in.getEventType()) { case XMLStreamConstants.CDATA: case XMLStreamConstants.CHARACTERS: final String text = this.in.getText(); if (Property.hasValue(text)) { map.put("xmlText" + ++textIndex, text); } break; case XMLStreamConstants.SPACE: break; case XMLStreamConstants.START_ELEMENT: elementIndex++; final String tagName = this.in.getLocalName(); final Object value = readElement(); final Object oldValue = map.get(tagName); if (oldValue == null) { map.put(tagName, value); } else { List<Object> list; if (oldValue instanceof List) { list = (List<Object>)oldValue; } else { list = new ArrayList<>(); list.add(oldValue); map.put(tagName, list); } list.add(value); } break; case XMLStreamConstants.COMMENT: break; default: System.err.println(this.in.getEventType() + " " + this.in.getText()); break; } } if (elementIndex == 0) { if (textIndex > 0) { final StringBuilder fullText = new StringBuilder(); for (final Object text : map.values()) { fullText.append(text); } return fullText.toString(); } } return map; } @SuppressWarnings("unchecked") private MapEx readMap() { final String name = this.in.getLocalName(); final MapEx map = new NamedLinkedHashMapEx(name); int textIndex = 0; while (this.in.next() != XMLStreamConstants.END_ELEMENT) { switch (this.in.getEventType()) { case XMLStreamConstants.CDATA: case XMLStreamConstants.CHARACTERS: final String text = this.in.getText(); if (Property.hasValue(text)) { map.put("xmlText" + ++textIndex, text); } break; case XMLStreamConstants.SPACE: break; case XMLStreamConstants.START_ELEMENT: final String tagName = this.in.getLocalName(); final Object value = readElement(); final Object oldValue = map.get(tagName); if (oldValue == null) { map.put(tagName, value); } else { List<Object> list; if (oldValue instanceof List) { list = (List<Object>)oldValue; } else { list = new ArrayList<>(); list.add(oldValue); map.put(tagName, list); } list.add(value); } break; default: break; } } return map; } }