001/* 002 * Copyright 2006-2007 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.batch.item.xml.stax; 018 019import java.util.NoSuchElementException; 020 021import javax.xml.namespace.QName; 022import javax.xml.stream.XMLEventFactory; 023import javax.xml.stream.XMLEventReader; 024import javax.xml.stream.XMLStreamException; 025import javax.xml.stream.events.EndDocument; 026import javax.xml.stream.events.EndElement; 027import javax.xml.stream.events.StartDocument; 028import javax.xml.stream.events.StartElement; 029import javax.xml.stream.events.XMLEvent; 030 031import org.springframework.dao.DataAccessResourceFailureException; 032 033/** 034 * Default implementation of {@link FragmentEventReader} 035 * 036 * @author Robert Kasanicky 037 */ 038public class DefaultFragmentEventReader extends AbstractEventReaderWrapper implements FragmentEventReader { 039 040 // true when the next event is the StartElement of next fragment 041 private boolean startFragmentFollows = false; 042 043 // true when the next event is the EndElement of current fragment 044 private boolean endFragmentFollows = false; 045 046 // true while cursor is inside fragment 047 private boolean insideFragment = false; 048 049 // true when reader should behave like the cursor was at the end of document 050 private boolean fakeDocumentEnd = false; 051 052 private StartDocument startDocumentEvent = null; 053 054 private EndDocument endDocumentEvent = null; 055 056 // fragment root name is remembered so that the matching closing element can 057 // be identified 058 private QName fragmentRootName = null; 059 060 // counts the occurrences of current fragmentRootName (increased for 061 // StartElement, decreased for EndElement) 062 private int matchCounter = 0; 063 064 /** 065 * Caches the StartDocument event for later use. 066 * @param wrappedEventReader the original wrapped event reader 067 */ 068 public DefaultFragmentEventReader(XMLEventReader wrappedEventReader) { 069 super(wrappedEventReader); 070 try { 071 startDocumentEvent = (StartDocument) wrappedEventReader.peek(); 072 } 073 catch (XMLStreamException e) { 074 throw new DataAccessResourceFailureException("Error reading start document from event reader", e); 075 } 076 077 endDocumentEvent = XMLEventFactory.newInstance().createEndDocument(); 078 } 079 080 @Override 081 public void markStartFragment() { 082 startFragmentFollows = true; 083 fragmentRootName = null; 084 } 085 086 @Override 087 public boolean hasNext() { 088 try { 089 if (peek() != null) { 090 return true; 091 } 092 } 093 catch (XMLStreamException e) { 094 throw new DataAccessResourceFailureException("Error reading XML stream", e); 095 } 096 return false; 097 } 098 099 @Override 100 public Object next() { 101 try { 102 return nextEvent(); 103 } 104 catch (XMLStreamException e) { 105 throw new DataAccessResourceFailureException("Error reading XML stream", e); 106 } 107 } 108 109 @Override 110 public XMLEvent nextEvent() throws XMLStreamException { 111 if (fakeDocumentEnd) { 112 throw new NoSuchElementException(); 113 } 114 XMLEvent event = wrappedEventReader.peek(); 115 XMLEvent proxyEvent = alterEvent(event, false); 116 checkFragmentEnd(proxyEvent); 117 if (event == proxyEvent) { 118 wrappedEventReader.nextEvent(); 119 } 120 121 return proxyEvent; 122 } 123 124 /** 125 * Sets the endFragmentFollows flag to true if next event is the last event 126 * of the fragment. 127 * @param event peek() from wrapped event reader 128 */ 129 private void checkFragmentEnd(XMLEvent event) { 130 if (event.isStartElement() && ((StartElement) event).getName().equals(fragmentRootName)) { 131 matchCounter++; 132 } 133 else if (event.isEndElement() && ((EndElement) event).getName().equals(fragmentRootName)) { 134 matchCounter--; 135 if (matchCounter == 0) { 136 endFragmentFollows = true; 137 } 138 } 139 } 140 141 /** 142 * @param event peek() from wrapped event reader 143 * @param peek if true do not change the internal state 144 * @return StartDocument event if peek() points to beginning of fragment 145 * EndDocument event if cursor is right behind the end of fragment original 146 * event otherwise 147 */ 148 private XMLEvent alterEvent(XMLEvent event, boolean peek) { 149 if (startFragmentFollows) { 150 fragmentRootName = ((StartElement) event).getName(); 151 if (!peek) { 152 startFragmentFollows = false; 153 insideFragment = true; 154 } 155 return startDocumentEvent; 156 } 157 else if (endFragmentFollows) { 158 if (!peek) { 159 endFragmentFollows = false; 160 insideFragment = false; 161 fakeDocumentEnd = true; 162 } 163 return endDocumentEvent; 164 } 165 return event; 166 } 167 168 @Override 169 public XMLEvent peek() throws XMLStreamException { 170 if (fakeDocumentEnd) { 171 return null; 172 } 173 return alterEvent(wrappedEventReader.peek(), true); 174 } 175 176 /** 177 * Finishes reading the fragment in case the fragment was processed without 178 * being read until the end. 179 */ 180 @Override 181 public void markFragmentProcessed() { 182 if (insideFragment|| startFragmentFollows) { 183 try { 184 while (!(nextEvent() instanceof EndDocument)) { 185 // just read all events until EndDocument 186 } 187 } 188 catch (XMLStreamException e) { 189 throw new DataAccessResourceFailureException("Error reading XML stream", e); 190 } 191 } 192 fakeDocumentEnd = false; 193 } 194 195 @Override 196 public void reset() { 197 insideFragment = false; 198 startFragmentFollows = false; 199 endFragmentFollows = false; 200 fakeDocumentEnd = false; 201 fragmentRootName = null; 202 matchCounter = 0; 203 } 204 205}