Tried to do a performance and memory test; they fail or pass depending on the order the Documents are tested. Could someone more experienced tell me what am i doing wrong? The performance test i expected to fail (since reflection is much slower than override), but the memory test i did not expect to depend on order.
import java.io.BufferedReader; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.ThreadMXBean; import java.lang.reflect.Method; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import javax.swing.event.DocumentEvent; import javax.swing.event.UndoableEditEvent; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultStyledDocument; import javax.swing.text.DefaultStyledDocument.ElementSpec; import javax.swing.text.GapContent; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; public class DocumentTest { public DocumentTest() { } @BeforeClass public static void setUpClass() throws Exception { } @AfterClass public static void tearDownClass() throws Exception { } @Before public void setUp() { } @After public void tearDown() { } OldTestDocument oldDoc = new OldTestDocument(); NewTestDocument newDoc = new NewTestDocument(); /** * Test of insert method performance and memory */ @Test public void testInsert() throws Exception { System.out.println("insert"); List<ElementSpec> l = new ArrayList<>(); try (BufferedReader r = Files.newBufferedReader(Paths.get("War and Peace.txt"), Charset.forName("us-ascii"))) { while (r.ready()) { String line = r.readLine(); l.add(new ElementSpec(null, ElementSpec.StartTagType)); l.add(new ElementSpec(null, ElementSpec.ContentType, line.toCharArray(), 0, line.length())); l.add(new ElementSpec(null, ElementSpec.EndTagType)); } } //every styled document must end with a 'implied newline' l.add(new ElementSpec(null, ElementSpec.ContentType, new char[]{'\n'}, 0, 1)); ElementSpec[] arr = (ElementSpec[]) l.toArray(new ElementSpec[0]); long time, mem, elapsedTime1, elapsedTime2, memory1, memory2; ThreadMXBean t = ManagementFactory.getThreadMXBean(); MemoryMXBean m = ManagementFactory.getMemoryMXBean(); //using non-heap memory because we are testing the memory impact of the stringbuilder //inside DefaultStyledDocument.insert(int offset, ElementSpec[] data) //to switch the order exchange the next 6 lines of code with the other six lines below m.gc(); mem = m.getNonHeapMemoryUsage().getUsed(); time = t.getCurrentThreadCpuTime(); oldDoc.insert(0, arr); elapsedTime2 = t.getCurrentThreadCpuTime() - time; memory2 = m.getNonHeapMemoryUsage().getUsed() - mem; m.gc(); mem = m.getNonHeapMemoryUsage().getUsed(); time = t.getCurrentThreadCpuTime(); newDoc.insert(0, arr); elapsedTime1 = t.getCurrentThreadCpuTime() - time; memory1 = m.getNonHeapMemoryUsage().getUsed() - mem; System.out.println("new style insert time : " + elapsedTime1 + " memory used : " + memory1); System.out.println("old style insert time : " + elapsedTime2 + " memory used : " + memory2); assert memory1 < memory2 : "old style insert took less memory than new style insert!"; assert elapsedTime1 < elapsedTime2 : "old style insert took less time than new style insert!"; } class OldTestDocument extends DefaultStyledDocument { @Override public void insert(int offset, ElementSpec[] data) throws BadLocationException { super.insert(offset, data); } } final class NewTestDocument extends DefaultStyledDocument { private final Method replace; private final Object[] bulkArgs = new Object[4]; public NewTestDocument() { super(); try { Class[] args = new Class[]{Integer.TYPE, Integer.TYPE, Object.class, Integer.TYPE}; replace = GapContent.class.getSuperclass().getDeclaredMethod("replace", args); replace.setAccessible(true); } catch (Exception ex) { throw new AssertionError(ex); } } @Override public void insert(int offset, ElementSpec[] data) throws BadLocationException { if (offset > getLength() || offset < 0) { throw new BadLocationException("Invalid insert", offset); } if (data == null || data.length == 0) { return; } writeLock(); try { Content content = getContent(); //since instead of doing normal string insert we are going to use the gapvector directly, we don't do this //UndoableEdit cEdit = content.insertString(offset, sb.toString()); int charArraysSize = 0; int index = offset; for (ElementSpec e : data) { if (e.getLength() > 0) { charArraysSize += e.getLength(); bulkArgs[0] = index; bulkArgs[1] = e.getOffset(); bulkArgs[2] = e.getArray(); bulkArgs[3] = e.getLength(); index += e.getLength(); try { replace.invoke(content, bulkArgs); } catch (Exception ex) { throw new AssertionError(ex); } } } if (charArraysSize == 0) { return; } //created the undoable edit corresponding to the whole inserted string //this can't be created since it is package default in GapContent //UndoableEdit cEdit = new InsertUndo(offset, charArraysSize); DefaultDocumentEvent evnt = new DefaultDocumentEvent(offset, charArraysSize, DocumentEvent.EventType.INSERT); //evnt.addEdit(cEdit); buffer.insert(offset, charArraysSize, data, evnt); // update bidi (possibly) super.insertUpdate(evnt, null); // notify the listeners evnt.end(); fireInsertUpdate(evnt); fireUndoableEditUpdate(new UndoableEditEvent(this, evnt)); } finally { writeUnlock(); } } } }