class DefaultFragmentEventReader extends AbstractEventReaderWrapper implements FragmentEventReader {

	// true when the next event is the StartElement of next fragment
	private boolean startFragmentFollows = false;

	// true when the next event is the EndElement of current fragment
	private boolean endFragmentFollows = false;

	// true while cursor is inside fragment
	private boolean insideFragment = false;

	// true when reader should behave like the cursor was at the end of document
	private boolean fakeDocumentEnd = false;

	private final StartDocument startDocumentEvent;

	private final EndDocument endDocumentEvent;

	// fragment root name is remembered so that the matching closing element can
	// be identified
	private QName fragmentRootName = null;

	// counts the occurrences of current fragmentRootName (increased for
	// StartElement, decreased for EndElement)
	private int matchCounter = 0;

	/**
	 * Caches the StartDocument event for later use.
	 * @param wrappedEventReader the original wrapped event reader
	 */
	public DefaultFragmentEventReader(XMLEventReader wrappedEventReader) {
		super(wrappedEventReader);
		try {
			startDocumentEvent = (StartDocument) wrappedEventReader.peek();
		}
		catch (XMLStreamException e) {
			throw new ItemStreamException("Error reading start document from event reader", e);
		}

		endDocumentEvent = XMLEventFactory.newInstance().createEndDocument();
	}

	@Override
	public void markStartFragment() {
		startFragmentFollows = true;
		fragmentRootName = null;
	}

	@Override
	public boolean hasNext() {
		try {
			if (peek() != null) {
				return true;
			}
		}
		catch (XMLStreamException e) {
			throw new ItemStreamException("Error reading XML stream", e);
		}
		return false;
	}

	@Override
	public Object next() {
		try {
			return nextEvent();
		}
		catch (XMLStreamException e) {
			throw new ItemStreamException("Error reading XML stream", e);
		}
	}

	@Override
	public XMLEvent nextEvent() throws XMLStreamException {
		if (fakeDocumentEnd) {
			throw new NoSuchElementException();
		}
		XMLEvent event = wrappedEventReader.peek();
		XMLEvent proxyEvent = alterEvent(event, false);
		checkFragmentEnd(proxyEvent);
		if (event == proxyEvent) {
			wrappedEventReader.nextEvent();
		}

		return proxyEvent;
	}

	/**
	 * Sets the endFragmentFollows flag to true if next event is the last event of the
	 * fragment.
	 * @param event peek() from wrapped event reader
	 */
	private void checkFragmentEnd(XMLEvent event) {
		if (event.isStartElement() && ((StartElement) event).getName().equals(fragmentRootName)) {
			matchCounter++;
		}
		else if (event.isEndElement() && ((EndElement) event).getName().equals(fragmentRootName)) {
			matchCounter--;
			if (matchCounter == 0) {
				endFragmentFollows = true;
			}
		}
	}

	/**
	 * @param event peek() from wrapped event reader
	 * @param peek if true do not change the internal state
	 * @return StartDocument event if peek() points to beginning of fragment EndDocument
	 * event if cursor is right behind the end of fragment original event otherwise
	 */
	private XMLEvent alterEvent(XMLEvent event, boolean peek) {<FILL_FUNCTION_BODY>}

	@Override
	public XMLEvent peek() throws XMLStreamException {
		if (fakeDocumentEnd) {
			return null;
		}
		return alterEvent(wrappedEventReader.peek(), true);
	}

	/**
	 * Finishes reading the fragment in case the fragment was processed without being read
	 * until the end.
	 */
	@Override
	public void markFragmentProcessed() {
		if (insideFragment || startFragmentFollows) {
			try {
				while (!(nextEvent() instanceof EndDocument)) {
					// just read all events until EndDocument
				}
			}
			catch (XMLStreamException e) {
				throw new ItemStreamException("Error reading XML stream", e);
			}
		}
		fakeDocumentEnd = false;
	}

	@Override
	public void reset() {
		insideFragment = false;
		startFragmentFollows = false;
		endFragmentFollows = false;
		fakeDocumentEnd = false;
		fragmentRootName = null;
		matchCounter = 0;
	}

}

		if (startFragmentFollows) {
			fragmentRootName = ((StartElement) event).getName();
			if (!peek) {
				startFragmentFollows = false;
				insideFragment = true;
			}
			return startDocumentEvent;
		}
		else if (endFragmentFollows) {
			if (!peek) {
				endFragmentFollows = false;
				insideFragment = false;
				fakeDocumentEnd = true;
			}
			return endDocumentEvent;
		}
		return event;
	
<methods>public void <init>(javax.xml.stream.XMLEventReader) ,public void close() throws javax.xml.stream.XMLStreamException,public java.lang.String getElementText() throws javax.xml.stream.XMLStreamException,public java.lang.Object getProperty(java.lang.String) throws java.lang.IllegalArgumentException,public boolean hasNext() ,public java.lang.Object next() ,public javax.xml.stream.events.XMLEvent nextEvent() throws javax.xml.stream.XMLStreamException,public javax.xml.stream.events.XMLEvent nextTag() throws javax.xml.stream.XMLStreamException,public javax.xml.stream.events.XMLEvent peek() throws javax.xml.stream.XMLStreamException,public void remove() <variables>protected javax.xml.stream.XMLEventReader wrappedEventReader