/* * Hibernate, Relational Persistence for Idiomatic Java * * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.boot.jaxb.internal.stax; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.XMLEvent; /** * Buffers XML events for later re-reading * * Note, copied from the uPortal project by permission of author. See * https://github.com/Jasig/uPortal/blob/master/uportal-war/src/main/java/org/jasig/portal/xml/stream/BufferedXMLEventReader.java * * @author Eric Dalquist */ public class BufferedXMLEventReader extends BaseXMLEventReader { private final LinkedList<XMLEvent> eventBuffer = new LinkedList<XMLEvent>(); private int eventLimit; private ListIterator<XMLEvent> bufferReader; /** * Create new buffering reader, no buffering is done until {@link #mark(int)} is called. */ public BufferedXMLEventReader(XMLEventReader reader) { super(reader); } /** * Create new buffering reader. Calls {@link #mark(int)} with the specified event limit * @see #mark(int) */ public BufferedXMLEventReader(XMLEventReader reader, int eventLimit) { super(reader); this.eventLimit = eventLimit; } /** * @return A copy of the current buffer */ public List<XMLEvent> getBuffer() { return new ArrayList<XMLEvent>(this.eventBuffer); } @Override protected XMLEvent internalNextEvent() throws XMLStreamException { //If there is an iterator to read from reset was called, use the iterator //until it runs out of events. if (this.bufferReader != null) { final XMLEvent event = this.bufferReader.next(); //If nothing left in the iterator, remove the reference and fall through to direct reading if (!this.bufferReader.hasNext()) { this.bufferReader = null; } return event; } //Get the next event from the underlying reader final XMLEvent event = this.getParent().nextEvent(); //if buffering add the event if (this.eventLimit != 0) { this.eventBuffer.offer(event); //If limited buffer size and buffer is too big trim the buffer. if (this.eventLimit > 0 && this.eventBuffer.size() > this.eventLimit) { this.eventBuffer.poll(); } } return event; } @Override public boolean hasNext() { return this.bufferReader != null || super.hasNext(); } @Override public XMLEvent peek() throws XMLStreamException { if (this.bufferReader != null) { final XMLEvent event = this.bufferReader.next(); this.bufferReader.previous(); //move the iterator back return event; } return super.peek(); } /** * Same as calling {@link #mark(int)} with -1. */ public void mark() { this.mark(-1); } /** * Start buffering events * @param eventLimit the maximum number of events to buffer. -1 will buffer all events, 0 will buffer no events. */ public void mark(int eventLimit) { this.eventLimit = eventLimit; //Buffering no events now, clear the buffer and buffered reader if (this.eventLimit == 0) { this.eventBuffer.clear(); this.bufferReader = null; } //Buffering limited set of events, lets trim the buffer if needed else if (this.eventLimit > 0) { //If there is an iterator check its current position and calculate the new iterator start position int iteratorIndex = 0; if (this.bufferReader != null) { final int nextIndex = this.bufferReader.nextIndex(); iteratorIndex = Math.max( 0, nextIndex - ( this.eventBuffer.size() - this.eventLimit ) ); } //Trim the buffer until it is not larger than the limit while (this.eventBuffer.size() > this.eventLimit) { this.eventBuffer.poll(); } //If there is an iterator re-create it using the newly calculated index if (this.bufferReader != null) { this.bufferReader = this.eventBuffer.listIterator(iteratorIndex); } } } /** * Reset the reader to these start of the buffered events. */ public void reset() { if (this.eventBuffer.isEmpty()) { this.bufferReader = null; } else { this.bufferReader = this.eventBuffer.listIterator(); } } @Override public void close() throws XMLStreamException { this.mark(0); super.close(); } /** * @return The number of events in the buffer. */ public int bufferSize() { return this.eventBuffer.size(); } /** * If reading from the buffer after a {@link #reset()} call an {@link IllegalStateException} will be thrown. */ @Override public void remove() { if (this.bufferReader != null && this.bufferReader.hasNext()) { throw new IllegalStateException("Cannot remove a buffered element"); } super.remove(); } }