Permalink
Browse files

Added chained buffers.

  • Loading branch information...
1 parent 292cc28 commit e50fec8396f093203e3de0372a064139e30e93cc @jroper jroper committed Jul 2, 2011
Showing with 484 additions and 95 deletions.
  1. +4 −0 .gitignore
  2. +85 −0 src/java/com/opensymphony/module/sitemesh/DefaultSitemeshBuffer.java
  3. +2 −14 src/java/com/opensymphony/module/sitemesh/PageParser.java
  4. +58 −0 src/java/com/opensymphony/module/sitemesh/SitemeshBuffer.java
  5. +38 −0 src/java/com/opensymphony/module/sitemesh/SitemeshBufferFragment.java
  6. +32 −0 src/java/com/opensymphony/module/sitemesh/SitemeshBufferWriter.java
  7. +28 −0 src/java/com/opensymphony/module/sitemesh/SitemeshWriter.java
  8. +9 −24 src/java/com/opensymphony/module/sitemesh/filter/Buffer.java
  9. +2 −0 src/java/com/opensymphony/module/sitemesh/filter/BufferedContent.java
  10. +2 −1 src/java/com/opensymphony/module/sitemesh/filter/PageResponseWrapper.java
  11. +25 −1 src/java/com/opensymphony/module/sitemesh/filter/RoutablePrintWriter.java
  12. +29 −0 src/java/com/opensymphony/module/sitemesh/filter/SitemeshPrintWriter.java
  13. +19 −2 src/java/com/opensymphony/module/sitemesh/multipass/MultipassReplacementPageParser.java
  14. +15 −5 src/java/com/opensymphony/module/sitemesh/parser/FastPageParser.java
  15. +19 −4 src/java/com/opensymphony/module/sitemesh/parser/HTMLPageParser.java
  16. +6 −6 src/java/com/opensymphony/module/sitemesh/parser/SuperFastHtmlPage.java
  17. +13 −6 src/java/com/opensymphony/module/sitemesh/parser/SuperFastPage.java
  18. +12 −12 src/java/com/opensymphony/module/sitemesh/parser/SuperFastSimplePageParser.java
  19. +8 −5 src/java/com/opensymphony/module/sitemesh/taglib/page/ApplyDecoratorTag.java
  20. +2 −1 src/java/com/opensymphony/sitemesh/ContentProcessor.java
  21. +3 −6 src/java/com/opensymphony/sitemesh/compatability/PageParser2ContentProcessor.java
  22. +2 −1 src/java/com/opensymphony/sitemesh/webapp/ContentBufferingResponse.java
  23. +61 −0 src/test/com/opensymphony/module/sitemesh/chaining/ChainingBufferTest.java
  24. +2 −1 src/test/com/opensymphony/module/sitemesh/multipass/DivExtractingPageParserTest.java
  25. +3 −2 src/test/com/opensymphony/module/sitemesh/parser/HTMLPageParserTest.java
  26. +5 −4 src/test/com/opensymphony/module/sitemesh/parser/ParserPerformanceComparison.java
View
@@ -1,2 +1,6 @@
build
+out
+*.iml
+*.ipr
+*.iws
dist
@@ -0,0 +1,85 @@
+package com.opensymphony.module.sitemesh;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The default implementation of sitemesh buffer
+ */
+public class DefaultSitemeshBuffer implements SitemeshBuffer {
+
+ private final char[] buffer;
+ private final int length;
+ private final List<SitemeshBufferFragment> chainedBuffers;
+
+ public DefaultSitemeshBuffer(char[] buffer) {
+ this(buffer, buffer.length);
+ }
+
+ public DefaultSitemeshBuffer(char[] buffer, int length) {
+ this(buffer, length, Collections.<SitemeshBufferFragment>emptyList());
+ }
+
+ public DefaultSitemeshBuffer(char[] buffer, int length, List<SitemeshBufferFragment> chainedBuffers) {
+ this.buffer = buffer;
+ this.length = length;
+ this.chainedBuffers = new ArrayList<SitemeshBufferFragment>(chainedBuffers);
+ Collections.sort(chainedBuffers);
+ }
+
+ public void writeTo(Writer writer, int start, int length) throws IOException {
+ int pos = start;
+ for (SitemeshBufferFragment fragment : chainedBuffers) {
+ if (fragment.getPosition() < pos) {
+ continue;
+ }
+ if (fragment.getPosition() >= start + length) {
+ break;
+ }
+ // Write the buffer up to the fragment
+ writer.write(buffer, pos, fragment.getPosition() - pos);
+ // Write the fragment
+ fragment.getBuffer().writeTo(writer, fragment.getStart(), fragment.getLength());
+ // increment pos
+ pos = fragment.getPosition();
+ }
+ // Write out the remaining buffer
+ if (pos < start + length) {
+ writer.write(buffer, pos, (start + length) - pos);
+ }
+ }
+
+ public int getTotalLength() {
+ return getTotalLength(0, length);
+ }
+
+ public int getTotalLength(int start, int length) {
+ int total = length;
+
+ for (SitemeshBufferFragment fragment : chainedBuffers) {
+ if (fragment.getPosition() < start) {
+ continue;
+ }
+ if (fragment.getPosition() > start + length) {
+ break;
+ }
+ total += fragment.getBuffer().getTotalLength(fragment.getStart(), fragment.getLength());
+ }
+ return total;
+ }
+
+ public int getBufferLength() {
+ return length;
+ }
+
+ public char[] getCharArray() {
+ return buffer;
+ }
+
+ public boolean hasFragments() {
+ return !chainedBuffers.isEmpty();
+ }
+}
@@ -25,24 +25,12 @@
* @version $Revision: 1.2 $
*/
public interface PageParser {
-
- /**
- * This builds a Page.
- *
- * @param data The data for the page. Note, this array may be larger than the length of the content.
- * @param length The length of the page.
- * @return The parsed page
- * @throws IOException if an error occurs
- * @since 2.5
- */
- Page parse(char[] data, int length) throws IOException;
-
/**
* This builds a Page.
*
- * @param data The data for the page.
+ * @param buffer The buffer for the page.
* @return The parsed page
* @throws IOException if an error occurs
*/
- Page parse(char[] data) throws IOException;
+ Page parse(SitemeshBuffer buffer) throws IOException;
}
@@ -0,0 +1,58 @@
+package com.opensymphony.module.sitemesh;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * A potentially chained sitemesh buffer
+ */
+public interface SitemeshBuffer {
+
+ /**
+ * Get the char array for this buffer. This array may be longer than the length of the content, you must use
+ * getBufferLength() in combination with this method.
+ *
+ * @return The char array for this buffer
+ */
+ char[] getCharArray();
+
+ /**
+ * Get the length of the buffered content.
+ *
+ * @return The length of the buffered content.
+ */
+ int getBufferLength();
+
+ /**
+ * Get the total length of the buffered content, including the length of any chained buffers.
+ *
+ * @return The total length.
+ */
+ int getTotalLength();
+
+ /**
+ * Get the total length of the buffered content, including chained buffers from start to length
+ *
+ * @param start Where to start counting the length from
+ * @param length Where to finish
+ * @return THe total length in the given range
+ */
+ int getTotalLength(int start, int length);
+
+ /**
+ * Write this buffer, and any chained sub buffers in the given range, out to the given writer
+ *
+ * @param start The position to start writing from
+ * @param length The length to write
+ * @param writer The writer to write to
+ * @throws IOException If an error occurred
+ */
+ void writeTo(Writer writer, int start, int length) throws IOException;
+
+ /**
+ * Whether the buffer has fragments or not
+ *
+ * @return True if it has fragments
+ */
+ boolean hasFragments();
+}
@@ -0,0 +1,38 @@
+package com.opensymphony.module.sitemesh;
+
+/**
+ * A fragment of a sitemesh buffer
+ */
+public class SitemeshBufferFragment implements Comparable<SitemeshBufferFragment> {
+ private final SitemeshBuffer buffer;
+ private final int position;
+ private final int start;
+ private final int length;
+
+ public SitemeshBufferFragment(SitemeshBuffer buffer, int start, int length, int position) {
+ this.buffer = buffer;
+ this.start = start;
+ this.length = length;
+ this.position = position;
+ }
+
+ public SitemeshBuffer getBuffer() {
+ return buffer;
+ }
+
+ public int getStart() {
+ return start;
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public int compareTo(SitemeshBufferFragment o) {
+ return o.position - position;
+ }
+}
@@ -0,0 +1,32 @@
+package com.opensymphony.module.sitemesh;
+
+import com.opensymphony.module.sitemesh.util.CharArrayWriter;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A char array writer that caches other sitemesh buffers written to it, so that it doesn't have to continually copy
+ * them from buffer to buffer.
+ */
+public class SitemeshBufferWriter extends CharArrayWriter implements SitemeshWriter {
+
+ private final List<SitemeshBufferFragment> fragments = new ArrayList<SitemeshBufferFragment>();
+
+ public SitemeshBufferWriter() {
+ }
+
+ public SitemeshBufferWriter(int initialSize) {
+ super(initialSize);
+ }
+
+ public boolean writeSitemeshBuffer(SitemeshBuffer sitemeshBuffer, int start, int length) throws IOException {
+ fragments.add(new SitemeshBufferFragment(sitemeshBuffer, start, length, count));
+ return false;
+ }
+
+ public SitemeshBuffer getSitemeshBuffer() {
+ return new DefaultSitemeshBuffer(buf, count, fragments);
+ }
+}
@@ -0,0 +1,28 @@
+package com.opensymphony.module.sitemesh;
+
+import java.io.IOException;
+
+/**
+ * A sitemesh buffer writer. Provides the ability to defer the writing of a page body until it is finally written to the
+ * stream, so it isn't copied from buffer to buffer
+ */
+public interface SitemeshWriter {
+ /**
+ * Write a sitemesh buffer to the writer. This may not be written immediately, it may be stored and written later,
+ * when this buffer is written out to a writer.
+ *
+ * @param sitemeshBuffer The buffer to write
+ * @param start The place in the buffer to write from
+ * @param length The length of the buffer to write
+ * @return True if the buffer was written immediately, or false if it will be written later
+ * @throws IOException If an IOException occurred
+ */
+ boolean writeSitemeshBuffer(SitemeshBuffer sitemeshBuffer, int start, int length) throws IOException;
+
+ /**
+ * Get the underlying buffer for the writer
+ *
+ * @return The underlying buffer
+ */
+ public SitemeshBuffer getSitemeshBuffer();
+}
@@ -3,12 +3,10 @@
* distribution in the LICENSE.txt file. */
package com.opensymphony.module.sitemesh.filter;
-import com.opensymphony.module.sitemesh.Page;
-import com.opensymphony.module.sitemesh.PageParser;
+import com.opensymphony.module.sitemesh.*;
import com.opensymphony.module.sitemesh.util.FastByteArrayOutputStream;
import javax.servlet.ServletOutputStream;
-import java.io.CharArrayWriter;
import java.io.IOException;
import java.io.PrintWriter;
@@ -25,7 +23,7 @@
private final String encoding;
private final static TextEncoder TEXT_ENCODER = new TextEncoder();
- private AccessibleCharArrayWriter bufferedWriter;
+ private SitemeshBufferWriter bufferedWriter;
private FastByteArrayOutputStream bufferedStream;
private PrintWriter exposedWriter;
private ServletOutputStream exposedStream;
@@ -35,28 +33,27 @@ public Buffer(PageParser pageParser, String encoding) {
this.encoding = encoding;
}
- public BufferedContent getContents() throws IOException {
+ public SitemeshBuffer getContents() throws IOException {
if (bufferedWriter != null) {
- return new BufferedContent(bufferedWriter.getCharArray(), bufferedWriter.size());
+ return bufferedWriter.getSitemeshBuffer();
} else if (bufferedStream != null) {
- return new BufferedContent(TEXT_ENCODER.encode(bufferedStream.toByteArray(), encoding));
+ return new DefaultSitemeshBuffer(TEXT_ENCODER.encode(bufferedStream.toByteArray(), encoding));
} else {
- return new BufferedContent(new char[0]);
+ return new DefaultSitemeshBuffer(new char[0]);
}
}
public Page parse() throws IOException {
- BufferedContent content = getContents();
- return pageParser.parse(content.getBuffer(), content.getLength());
+ return pageParser.parse(getContents());
}
public PrintWriter getWriter() {
if (bufferedWriter == null) {
if (bufferedStream != null) {
throw new IllegalStateException("response.getWriter() called after response.getOutputStream()");
}
- bufferedWriter = new AccessibleCharArrayWriter(128);
- exposedWriter = new PrintWriter(bufferedWriter);
+ bufferedWriter = new SitemeshBufferWriter(128);
+ exposedWriter = new SitemeshPrintWriter(bufferedWriter);
}
return exposedWriter;
}
@@ -79,16 +76,4 @@ public void write(int b) {
public boolean isUsingStream() {
return bufferedStream != null;
}
-
- private static class AccessibleCharArrayWriter extends CharArrayWriter {
-
- private AccessibleCharArrayWriter(int initialSize) {
- super(initialSize);
- }
-
- public char[] getCharArray()
- {
- return buf;
- }
- }
}
@@ -1,5 +1,7 @@
package com.opensymphony.module.sitemesh.filter;
+import java.util.TreeMap;
+
public class BufferedContent {
private final char[] buffer;
private final int length;
@@ -5,6 +5,7 @@
import com.opensymphony.module.sitemesh.Page;
import com.opensymphony.module.sitemesh.PageParserSelector;
+import com.opensymphony.module.sitemesh.SitemeshBuffer;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
@@ -185,7 +186,7 @@ public boolean isUsingStream() {
return buffer != null && buffer.isUsingStream();
}
- public BufferedContent getContents() throws IOException {
+ public SitemeshBuffer getContents() throws IOException {
if (aborted || !parseablePage) {
return null;
} else {
Oops, something went wrong. Retry.

0 comments on commit e50fec8

Please sign in to comment.