blob: fe2f7fd7b664c7d057c49db7e3d5a1c59e63835e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.netbeans.modules.editor.structure.api;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import static junit.framework.TestCase.assertEquals;
import org.netbeans.editor.BaseDocument;
import org.netbeans.junit.NbTestCase;
import org.netbeans.modules.editor.structure.api.DocumentModel.DocumentChange;
import org.netbeans.modules.editor.structure.api.DocumentModel.DocumentModelTransactionCancelledException;
import org.netbeans.modules.editor.structure.spi.DocumentModelProvider;
/** DocumentModel unit tests
*
* @author Marek Fukala
*/
public class DocumentModelTest extends NbTestCase {
private DocumentModelProvider dmProvider;
public DocumentModelTest() {
super("document-model-test");
}
@Override
public void setUp() throws BadLocationException {
dmProvider = new FakeDocumentModelProvider();
}
//--------- test methods -----------
public void testModelBasis() throws DocumentModelException, BadLocationException {
//set the document content
Document doc = new BaseDocument(false, "text/plain");
doc.insertString(0,"abcde|fgh|ij|k",null); //4 elements should be created
DocumentModel model = new DocumentModel(doc, dmProvider);
assertNotNull(model.getDocument());
DocumentElement root = model.getRootElement();
assertNotNull(root);
assertNull(root.getParentElement());
//test if the content of the root elemnt equals the document content
assertTrue(root.getContent().equals(doc.getText(0, doc.getLength())));
List children = root.getChildren();
assertEquals(4, children.size());
DocumentElement first = root.getElement(0);
//check name and type
assertEquals("element0", first.getName());
assertEquals(FakeDocumentModelProvider.FAKE_ELEMENT_TYPE, first.getType());
//check content and offsets
assertEquals("abcde", first.getContent());
assertEquals(0, first.getStartOffset());
assertEquals(5, first.getEndOffset());
//check has no children
assertEquals(0, first.getElementCount());
}
public void testAddElementEvent() throws DocumentModelException, BadLocationException, InterruptedException {
Document doc = new BaseDocument(false, "text/plain");
final DocumentModel model = new DocumentModel(doc, dmProvider);
//listen to model
final List<DocumentElement> addedElements = new CopyOnWriteArrayList<DocumentElement>();
model.addDocumentModelListener(new DocumentModelListenerAdapter() {
@Override
public void documentElementAdded(DocumentElement de) {
addedElements.add(de);
}
});
//listen to element
final List<DocumentElement> addedElements2 = new CopyOnWriteArrayList<DocumentElement>();
model.getRootElement().addDocumentElementListener(new DocumentElementListenerAdapter() {
@Override
public void elementAdded(DocumentElementEvent e) {
addedElements2.add(e.getChangedChild());
}
});
model.addDocumentModelStateListener(new DocumentModelStateListenerAdapter() {
@Override
public void updateFinished() {
assertEquals(4, addedElements.size());
assertEquals(4, addedElements2.size());
assertEquals(4, model.getRootElement().getElementCount());
}
});
doc.insertString(0,"abcde|fgh|ij|k",null); //4 elements should be created
model.forceUpdate();
}
public void testMoreDocumentElementListeners() throws DocumentModelException, BadLocationException, InterruptedException {
Document doc = new BaseDocument(false, "text/plain");
DocumentModel model = new DocumentModel(doc, dmProvider);
DocumentElement root = model.getRootElement();
assertNotNull(root);
DocumentElementListener del1 = new DocumentElementListenerAdapter();
DocumentElementListener del2 = new DocumentElementListenerAdapter();
assertNull(root.deListener);
assertNull(root.deListeners);
root.addDocumentElementListener(del1);
assertTrue(root.deListener == del1);
assertNull(root.deListeners);
root.addDocumentElementListener(del2);
assertNull(root.deListener);
assertNotNull(root.deListeners);
assertEquals(2, root.deListeners.size());
//try to add twice
root.addDocumentElementListener(del2);
assertNull(root.deListener);
assertNotNull(root.deListeners);
assertEquals(2, root.deListeners.size());
root.removeDocumentElementListener(del2);
assertNull(root.deListener);
assertNotNull(root.deListeners);
assertEquals(1, root.deListeners.size());
root.removeDocumentElementListener(del1);
assertNull(root.deListener);
assertNotNull(root.deListeners);
assertEquals(0, root.deListeners.size());
}
public void testRemoveElementEvent() throws DocumentModelException, BadLocationException, InterruptedException {
Document doc = new BaseDocument(false, "text/plain");
doc.insertString(0,"abcde|fgh|ij|k",null); //4 elements should be created
final DocumentModel model = new DocumentModel(doc, dmProvider);
DocumentModelUtils.dumpModelElements(model);
DocumentModelUtils.dumpElementStructure(model.getRootElement());
//listen to model
final List<DocumentElement> removedElements = new CopyOnWriteArrayList<DocumentElement>();
model.addDocumentModelListener(new DocumentModelListenerAdapter() {
@Override
public void documentElementRemoved(DocumentElement de) {
removedElements.add(de);
}
});
//listen to element
final List<DocumentElement> removedElements2 = new CopyOnWriteArrayList<DocumentElement>();
model.getRootElement().addDocumentElementListener(new DocumentElementListenerAdapter() {
@Override
public void elementRemoved(DocumentElementEvent e) {
removedElements2.add(e.getChangedChild());
}
});
model.addDocumentModelStateListener(new DocumentModelStateListenerAdapter() {
@Override
public void updateFinished() {
assertEquals(4,removedElements2.size());
assertEquals(4, removedElements.size());
assertEquals(0, model.getRootElement().getElementCount());
}
});
doc.remove(0,doc.getLength());
model.forceUpdate();
}
public void testDocumentModelStateListener() throws DocumentModelException, BadLocationException {
Document doc = new BaseDocument(false, "text/plain");
final DocumentModel model = new DocumentModel(doc, dmProvider);
final State s = new State();
s.setValue(-1);
model.addDocumentModelStateListener(new DocumentModelStateListener() {
@Override
public void sourceChanged() {
assertEquals(-1, s.getValue());
s.setValue(0);
}
@Override
public void scanningStarted() {
assertEquals(0, s.getValue());
s.setValue(1);
}
@Override
public void updateStarted() {
assertEquals(1, s.getValue());
s.setValue(2);
}
@Override
public void updateFinished() {
assertEquals(2, s.getValue());
s.setValue(3);
synchronized (s) {
s.notifyAll();
}
}
});
doc.insertString(0, "blabla", null);
model.forceUpdate();
synchronized (s) {
try {
s.wait(10 * 1000); //10 sec
} catch (InterruptedException ex) {
assert false : "DocumentModelStateListener doesn't work properly";
}
}
assertEquals(3, s.getValue());
}
/**
* A Simple testing implementation of DocumentModelProvider interface
* used by DocumentModel unit tests.
*
* Creates elements according to the "|" separators: abcde|fghij|k|l|mnopqrstu|vwxyz
*
* @author Marek Fukala
*/
private static class FakeDocumentModelProvider implements DocumentModelProvider {
@Override
public void updateModel(DocumentModel.DocumentModelModificationTransaction dtm,
DocumentModel model, DocumentChange[] changes)
throws DocumentModelException, DocumentModelTransactionCancelledException {
try {
String text = model.getDocument().getText(0, model.getDocument().getLength());
int lastElementEnd = 0;
int elCount = 0;
ArrayList foundElements = new ArrayList();
for(int i = 0; i < text.length(); i++) {
if(text.charAt(i) == '|' || i == text.length() - 1) {
//create element if doesn't exist
DocumentElement test = model.getDocumentElement(lastElementEnd, i);
if( test == null) {
foundElements.add(dtm.addDocumentElement("element" + elCount, FAKE_ELEMENT_TYPE, Collections.EMPTY_MAP, lastElementEnd, i));
elCount++;
} else {
foundElements.add(test);
}
lastElementEnd = i;
}
}
//delete unexisting elements
ArrayList deleted = new ArrayList(model.getRootElement().getChildren());
deleted.removeAll(foundElements);
Iterator i = deleted.iterator();
while(i.hasNext()) {
DocumentElement de = (DocumentElement)i.next();
dtm.removeDocumentElement(de, false);
}
}catch(BadLocationException e) {
throw new DocumentModelException("error occured when creating elements",e);
}
}
public static final String FAKE_ELEMENT_TYPE = "fake element";
}
private static class DocumentModelListenerAdapter implements DocumentModelListener {
@Override
public void documentElementAdded(DocumentElement de) {
}
@Override
public void documentElementAttributesChanged(DocumentElement de) {
}
@Override
public void documentElementChanged(DocumentElement de) {
}
@Override
public void documentElementRemoved(DocumentElement de) {
}
}
private static class DocumentElementListenerAdapter implements DocumentElementListener {
@Override
public void attributesChanged(DocumentElementEvent e) {
}
@Override
public void childrenReordered(DocumentElementEvent e) {
}
@Override
public void contentChanged(DocumentElementEvent e) {
}
@Override
public void elementAdded(DocumentElementEvent e) {
}
@Override
public void elementRemoved(DocumentElementEvent e) {
}
}
private static class DocumentModelStateListenerAdapter implements DocumentModelStateListener {
@Override
public void sourceChanged() {
}
@Override
public void scanningStarted() {
}
@Override
public void updateStarted() {
}
@Override
public void updateFinished() {
}
}
private static class State {
private int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
}