/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.text.tests; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.List; import org.junit.Test; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentExtension; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.text.projection.ChildDocument; import org.eclipse.jface.text.projection.ChildDocumentManager; public class DocumentExtensionTest { static class Listener implements IDocumentListener { int fRepetitions= 1; private int fInvocations= 0; private boolean fIsPostNotificationSupported= true; @Override public void documentAboutToBeChanged(DocumentEvent e) { ++ fInvocations; } @Override public void documentChanged(DocumentEvent e) { if (fInvocations > fRepetitions) { fInvocations= 0; return; } if (e.getDocument() instanceof IDocumentExtension) { IDocumentExtension extension= (IDocumentExtension) e.getDocument(); Replace replace= getReplace(e); if (replace != null) { try { extension.registerPostNotificationReplace(this, replace); } catch (UnsupportedOperationException ex) { fIsPostNotificationSupported= false; } } } } /** * Returns the replace. * * @param e the document event * @return the replace */ protected Replace getReplace(DocumentEvent e) { return null; } public boolean isPostNotficationSupported() { return fIsPostNotificationSupported; } } static class Replace implements IDocumentExtension.IReplace { int fOffset; int fLength; String fText; public Replace() { } @Override public void perform(IDocument document, IDocumentListener owner) { try { document.replace(fOffset, fLength, fText); } catch (BadLocationException x) { assertTrue(false); } } } static class TestDocumentEvent extends DocumentEvent { public TestDocumentEvent(IDocument document, int offset, int length, String text) { super(document, offset, length, text); } public boolean isSameAs(DocumentEvent e) { return (e.getDocument() == getDocument() && e.getOffset() == getOffset() && e.getLength() == getLength() && ((e.getText() == null && getText() == null) || e.getText().equals(getText()))); } } static class TestDocumentListener implements IDocumentListener { private IDocument fDocument1; private List<TestDocumentEvent> fTrace1; private TestDocumentEvent fExpected1; private List<TestDocumentEvent> fTrace2; private TestDocumentEvent fExpected2; private boolean fPopped= false; public TestDocumentListener(IDocument d1, List<TestDocumentEvent> t1, List<TestDocumentEvent> t2) { fDocument1= d1; fTrace1= t1; fTrace2= t2; } @Override public void documentAboutToBeChanged(DocumentEvent received) { if (!fPopped) { fPopped= true; fExpected1= fTrace1.remove(0); fExpected2= fTrace2.remove(0); } TestDocumentEvent e= (received.getDocument() == fDocument1 ? fExpected1 : fExpected2); assertTrue(e.isSameAs(received)); } @Override public void documentChanged(DocumentEvent received) { TestDocumentEvent e= (received.getDocument() == fDocument1 ? fExpected1 : fExpected2); assertTrue(e.isSameAs(received)); fPopped= false; } } @Test public void testAppend() { Listener listener= new Listener() { @Override protected Replace getReplace(DocumentEvent e) { String t= e.getText(); if (t != null && t.length() > 0) { Replace r= new Replace(); r.fOffset= (e.getOffset() + t.length()); r.fLength= 0; r.fText= "x"; return r; } return null; } }; IDocument document= new Document(); document.addDocumentListener(listener); try { document.replace(0, 0, "c"); document.replace(0, 0, "b"); document.replace(0, 0, "a"); } catch (BadLocationException x) { assertTrue(false); } assertTrue("axbxcx".equals(document.get())); } @Test public void testRemove() { Listener listener= new Listener() { @Override protected Replace getReplace(DocumentEvent e) { String t= e.getText(); if (t == null || t.length() == 0) { Replace r= new Replace(); r.fOffset= e.getOffset(); r.fLength= 0; r.fText= "y"; return r; } return null; } }; IDocument document= new Document("abc"); document.addDocumentListener(listener); try { document.replace(2, 1, ""); document.replace(1, 1, ""); document.replace(0, 1, ""); } catch (BadLocationException x) { assertTrue(false); } assertTrue("yyy".equals(document.get())); } @Test public void testRepeatedAppend() { Listener listener= new Listener() { @Override protected Replace getReplace(DocumentEvent e) { String t= e.getText(); if (t != null && t.length() > 0) { Replace r= new Replace(); r.fOffset= (e.getOffset() + t.length()); r.fLength= 0; r.fText= "x"; return r; } return null; } }; listener.fRepetitions= 5; IDocument document= new Document(); document.addDocumentListener(listener); try { document.replace(0, 0, "c"); document.replace(0, 0, "b"); document.replace(0, 0, "a"); } catch (BadLocationException x) { assertTrue(false); } assertTrue("axxxxxbxxxxxcxxxxx".equals(document.get())); } private List<TestDocumentEvent> createTrace(IDocument document, int repetitions) { int i; List<TestDocumentEvent> trace= new ArrayList<>(); trace.add(new TestDocumentEvent(document, 0, 0, "c")); for (i= 0; i < repetitions; i++) trace.add(new TestDocumentEvent(document, 1 + i, 0, "x")); trace.add(new TestDocumentEvent(document, 0, 0, "b")); for (i= 0; i < repetitions; i++) trace.add(new TestDocumentEvent(document, 1 + i, 0, "x")); trace.add(new TestDocumentEvent(document, 0, 0, "a")); for (i= 0; i < repetitions; i++) trace.add(new TestDocumentEvent(document, 1 + i, 0, "x")); return trace; } private void internalTestChildDocument(boolean modifyParent, boolean postModifyParent, int repetitions) { IDocument childDocument= null; IDocument parentDocument= new Document(); ChildDocumentManager manager= new ChildDocumentManager(); try { childDocument= manager.createSlaveDocument(parentDocument); if (childDocument instanceof ChildDocument) { ChildDocument child= (ChildDocument) childDocument; child.setParentDocumentRange(0, parentDocument.getLength()); } } catch (BadLocationException x) { assertTrue(false); } TestDocumentListener l= new TestDocumentListener(parentDocument, createTrace(parentDocument, repetitions), createTrace(childDocument, repetitions)); parentDocument.addDocumentListener(l); childDocument.addDocumentListener(l); Listener modifier= new Listener() { @Override protected Replace getReplace(DocumentEvent e) { String t= e.getText(); if (t != null && t.length() > 0) { Replace r= new Replace(); r.fOffset= (e.getOffset() + t.length()); r.fLength= 0; r.fText= "x"; return r; } return null; } }; modifier.fRepetitions= repetitions; IDocument document= postModifyParent ? parentDocument : childDocument; document.addDocumentListener(modifier); document= modifyParent ? parentDocument : childDocument; try { document.replace(0, 0, "c"); if (!modifier.isPostNotficationSupported()) throw new UnsupportedOperationException(); document.replace(0, 0, "b"); if (!modifier.isPostNotficationSupported()) throw new UnsupportedOperationException(); document.replace(0, 0, "a"); if (!modifier.isPostNotficationSupported()) throw new UnsupportedOperationException(); } catch (BadLocationException x) { assertTrue(false); } } @Test public void testChildDocumentPP() { internalTestChildDocument(true, true, 1); } @Test public void testChildDocumentCC() { try { internalTestChildDocument(false, false, 1); } catch (UnsupportedOperationException x) { } } @Test public void testChildDocumentRepeatedPP() { internalTestChildDocument(true, true, 5); } @Test public void testChildDocumentRepeatedCC() { try { internalTestChildDocument(false, false, 5); } catch (UnsupportedOperationException e) { } } /** * Tests that this is not supported. */ @Test public void testChildDocumentPC() { try { internalTestChildDocument(true, false, 1); assertTrue(false); } catch (UnsupportedOperationException x) { } } @Test public void testChildDocumentCP() { internalTestChildDocument(false, true, 1); } /** * Tests that this is not supported. */ @Test public void testChildDocumentRepeatedPC() { try { internalTestChildDocument(true, false, 5); assertTrue(false); } catch (UnsupportedOperationException x) { } } @Test public void testChildDocumentRepeatedCP() { internalTestChildDocument(false, true, 5); } }