/* * ============================================================================= * * Copyright (c) 2011-2016, The THYMELEAF team (http://www.thymeleaf.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ============================================================================= */ package org.thymeleaf.engine; import java.io.IOException; /** * * @author Daniel Fernández * * @since 3.0.4 * */ class SSEThrottledTemplateWriter extends ThrottledTemplateWriter implements ISSEThrottledTemplateWriterControl { private final static char[] SSE_ID_PREFIX = "id: ".toCharArray(); private final static char[] SSE_EVENT_PREFIX = "event: ".toCharArray(); private final static char[] SSE_DATA_PREFIX = "data: ".toCharArray(); private String id = null; private String event = null; private boolean eventHasMeta = false; private boolean newEvent = true; SSEThrottledTemplateWriter(final String templateName, final TemplateFlowController flowController) { super(templateName, flowController); } public void startEvent(final String id, final String event) { this.newEvent = true; this.id = id; this.event = event; } private void doStartEvent() throws IOException { this.eventHasMeta = false; if (this.event != null) { // Write the "event" field if (this.event.indexOf('\n') != -1) { throw new IllegalArgumentException("Event for SSE event cannot contain a newline (\\n) character"); } super.write(SSE_EVENT_PREFIX); super.write(this.event.toCharArray()); super.write('\n'); this.eventHasMeta = true; } if (this.id != null) { // Write the "id" field if (this.id.indexOf('\n') != -1) { throw new IllegalArgumentException("ID for SSE event cannot contain a newline (\\n) character"); } super.write(SSE_ID_PREFIX); super.write(this.id.toCharArray()); super.write('\n'); this.eventHasMeta = true; } } public void endEvent() throws IOException { if (!this.newEvent) { super.write('\n'); super.write('\n'); } else if (this.eventHasMeta) { // If we only wrote meta, we still need an additional line feed to separate from the next event super.write('\n'); } } @Override public void write(final int c) throws IOException { if (this.newEvent) { doStartEvent(); super.write(SSE_DATA_PREFIX); this.newEvent = false; } super.write(c); if (c == '\n') { // This is a line feed, so we need to write the prefix afterwards super.write(SSE_DATA_PREFIX); } } @Override public void write(final String str) throws IOException { write(str, 0, str.length()); } @Override public void write(final String str, final int off, final int len) throws IOException { if (str == null) { throw new NullPointerException(); } else if ((off < 0) || (off > str.length()) || (len < 0) || ((off + len) > str.length()) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } if (len == 0) { // Even if we are writing nothing, we need to give the underlying buffered implementation the chance to // overflow... super.write(str, off, len); return; } if (this.newEvent) { doStartEvent(); super.write(SSE_DATA_PREFIX); this.newEvent = false; } char c; int i = off; int x = i; final int maxi = (off + len); while (i < maxi) { c = str.charAt(i++); if (c == '\n') { // This is a line feed, so we write everything until this point, then the prefix, then we continue super.write(str, x, (i - x)); super.write(SSE_DATA_PREFIX); x = i; } } // Finally we write whatever is left at the original buffer if (x < i) { super.write(str, x, (i - x)); } } @Override public void write(final char[] cbuf) throws IOException { write(cbuf, 0, cbuf.length); } @Override public void write(final char[] cbuf, final int off, final int len) throws IOException { if (cbuf == null) { throw new NullPointerException(); } else if ((off < 0) || (off > cbuf.length) || (len < 0) || ((off + len) > cbuf.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } if (len == 0) { // Even if we are writing nothing, we need to give the underlying buffered implementation the chance to // overflow... super.write(cbuf, off, len); return; } if (this.newEvent) { doStartEvent(); super.write(SSE_DATA_PREFIX); this.newEvent = false; } char c; int i = off; int x = i; final int maxi = (off + len); while (i < maxi) { c = cbuf[i++]; if (c == '\n') { // This is a line feed, so we write everything until this point, then the prefix, then we continue super.write(cbuf, x, (i - x)); super.write(SSE_DATA_PREFIX); x = i; } } // Finally we write whatever is left at the original buffer if (x < i) { super.write(cbuf, x, (i - x)); } } }