mirror of
https://github.com/sigmasternchen/xtext-core
synced 2025-03-15 16:28:56 +00:00
[#1262] Prevent infinite loop with NodeIterator.
Throw NullPointerException if null is passed to NodeIterator constructor to prevent iterator state where hasNext() and hasPrevious() always return true, but getNext() and getPrevious() always return null. Similarly, throw NPE for null passed to constructors of NodeIterable, BasicNodeIterator and BasicNodeIterable. Added unit tests for all four classes. Signed-off-by: Robert Lewis <rablewis@algo-pop.com>
This commit is contained in:
parent
da46e06388
commit
dc4699fb40
10 changed files with 440 additions and 4 deletions
|
@ -0,0 +1,21 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2020 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.nodemodel;
|
||||
|
||||
import org.eclipse.xtext.nodemodel.impl.AbstractNode;
|
||||
|
||||
/**
|
||||
* @author Robert Lewis - Initial contribution and API
|
||||
*/
|
||||
public class BasicNodeIterable extends org.eclipse.xtext.nodemodel.impl.BasicNodeIterable {
|
||||
|
||||
protected BasicNodeIterable(AbstractNode startWith) {
|
||||
super(startWith);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2020 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.nodemodel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.xtext.nodemodel.impl.AbstractNode;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Robert Lewis - Initial contribution and API
|
||||
*/
|
||||
public class BasicNodeIterableTest {
|
||||
|
||||
@Test
|
||||
public void forEachTest() {
|
||||
AbstractNode alpha = BasicNodeIteratorTest.nodeWithTwoSiblings();
|
||||
BasicNodeIterable iterable = new BasicNodeIterable(alpha);
|
||||
|
||||
List<String> tokens = new ArrayList<String>();
|
||||
for (INode node : iterable) {
|
||||
tokens.add(node.getText());
|
||||
}
|
||||
|
||||
Assert.assertEquals("alpha", tokens.get(0));
|
||||
Assert.assertEquals("beta", tokens.get(1));
|
||||
Assert.assertEquals("gamma", tokens.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forEachReverseTest() {
|
||||
AbstractNode alpha = BasicNodeIteratorTest.nodeWithTwoSiblings();
|
||||
BasicNodeIterable iterable = new BasicNodeIterable(alpha);
|
||||
|
||||
List<String> tokens = new ArrayList<String>();
|
||||
for (INode node : iterable.reverse()) {
|
||||
tokens.add(node.getText());
|
||||
}
|
||||
|
||||
Assert.assertEquals("gamma", tokens.get(0));
|
||||
Assert.assertEquals("beta", tokens.get(1));
|
||||
Assert.assertEquals("alpha", tokens.get(2));
|
||||
}
|
||||
|
||||
@Test(expected=NullPointerException.class)
|
||||
public void testStartWithNullThrowsNPE() {
|
||||
BasicNodeIterable iterable = new BasicNodeIterable(null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2020 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.nodemodel;
|
||||
|
||||
import org.eclipse.xtext.nodemodel.impl.AbstractNode;
|
||||
|
||||
/**
|
||||
* @author Robert Lewis - Initial contribution and API
|
||||
*/
|
||||
public class BasicNodeIterator extends org.eclipse.xtext.nodemodel.impl.BasicNodeIterator {
|
||||
|
||||
public BasicNodeIterator(AbstractNode startWith) {
|
||||
super(startWith);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2020 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.nodemodel;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.emf.ecore.impl.EObjectImpl;
|
||||
import org.eclipse.xtext.nodemodel.impl.AbstractNode;
|
||||
import org.eclipse.xtext.nodemodel.impl.NodeModelBuilder;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Robert Lewis - Initial contribution and API
|
||||
*/
|
||||
public class BasicNodeIteratorTest {
|
||||
|
||||
private static AbstractNode getSingleNode() {
|
||||
return new RootNode();
|
||||
}
|
||||
|
||||
public static AbstractNode nodeWithTwoSiblings() {
|
||||
NodeModelBuilder builder = new NodeModelBuilder();
|
||||
String text = "alpha beta gamma";
|
||||
|
||||
RootNode root = new RootNode();
|
||||
root.basicSetCompleteContent(text);
|
||||
|
||||
EObject alpha = new EObjectImpl() {};
|
||||
ILeafNode alphaNode = builder.newLeafNode(text.indexOf("alpha"), "alpha".length(), alpha, false, null, root);
|
||||
|
||||
EObject beta = new EObjectImpl() {};
|
||||
ILeafNode betaNode = builder.newLeafNode(text.indexOf("beta"), "beta".length(), beta, false, null, root);
|
||||
|
||||
EObject gamma = new EObjectImpl() {};
|
||||
ILeafNode gammaNode = builder.newLeafNode(text.indexOf("gamma"), "gamma".length(), gamma, false, null, root);
|
||||
|
||||
return root.basicGetFirstChild();
|
||||
|
||||
}
|
||||
|
||||
@Test(expected=NullPointerException.class)
|
||||
public void testStartWithNullThrowsNPE() {
|
||||
BasicNodeIterator it = new BasicNodeIterator(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleNodeHasNext() {
|
||||
AbstractNode single = getSingleNode();
|
||||
BasicNodeIterator it = new BasicNodeIterator(single);
|
||||
|
||||
Assert.assertTrue(it.hasNext());
|
||||
it.next();
|
||||
|
||||
Assert.assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test(expected=NoSuchElementException.class)
|
||||
public void testNextTooFarThrowsException() {
|
||||
AbstractNode single = getSingleNode();
|
||||
BasicNodeIterator it = new BasicNodeIterator(single);
|
||||
|
||||
it.next();
|
||||
|
||||
it.next();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterateThreeNodes() {
|
||||
AbstractNode alpha = nodeWithTwoSiblings();
|
||||
BasicNodeIterator it = new BasicNodeIterator(alpha);
|
||||
|
||||
Assert.assertTrue(it.hasNext());
|
||||
it.next();
|
||||
|
||||
Assert.assertTrue(it.hasNext());
|
||||
it.next();
|
||||
|
||||
Assert.assertTrue(it.hasNext());
|
||||
it.next();
|
||||
|
||||
Assert.assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleNodeHasPrevious() {
|
||||
AbstractNode single = getSingleNode();
|
||||
BasicNodeIterator it = new BasicNodeIterator(single);
|
||||
|
||||
Assert.assertTrue(it.hasPrevious());
|
||||
it.previous();
|
||||
|
||||
Assert.assertFalse(it.hasPrevious());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIteratePreviousStartsWithLastNode() {
|
||||
AbstractNode alpha = nodeWithTwoSiblings();
|
||||
BasicNodeIterator it = new BasicNodeIterator(alpha);
|
||||
|
||||
INode result = it.previous();
|
||||
|
||||
Assert.assertEquals("gamma", result.getText());
|
||||
}
|
||||
|
||||
@Test(expected=NoSuchElementException.class)
|
||||
public void testPreviousTooFarThrowsException() {
|
||||
AbstractNode single = getSingleNode();
|
||||
BasicNodeIterator it = new BasicNodeIterator(single);
|
||||
|
||||
it.previous();
|
||||
|
||||
it.previous();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterateThreeNodesInReverse() {
|
||||
AbstractNode alpha = nodeWithTwoSiblings();
|
||||
BasicNodeIterator it = new BasicNodeIterator(alpha);
|
||||
|
||||
Assert.assertTrue(it.hasPrevious());
|
||||
it.previous();
|
||||
|
||||
Assert.assertTrue(it.hasPrevious());
|
||||
it.previous();
|
||||
|
||||
Assert.assertTrue(it.hasPrevious());
|
||||
it.previous();
|
||||
|
||||
Assert.assertFalse(it.hasPrevious());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2020 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.nodemodel.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.xtext.nodemodel.INode;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Robert Lewis - Initial contribution and API
|
||||
*/
|
||||
public class NodeIterableTest {
|
||||
|
||||
@Test
|
||||
public void forEachTest() {
|
||||
INode alpha = NodeIteratorTest.nodeWithTwoSiblings();
|
||||
NodeIterable iterable = new NodeIterable(alpha);
|
||||
|
||||
List<String> tokens = new ArrayList<String>();
|
||||
for (INode node : iterable) {
|
||||
tokens.add(node.getText());
|
||||
}
|
||||
|
||||
Assert.assertEquals("alpha", tokens.get(0));
|
||||
Assert.assertEquals("beta", tokens.get(1));
|
||||
Assert.assertEquals("gamma", tokens.get(2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forEachReverseTest() {
|
||||
INode alpha = NodeIteratorTest.nodeWithTwoSiblings();
|
||||
NodeIterable iterable = new NodeIterable(alpha);
|
||||
|
||||
List<String> tokens = new ArrayList<String>();
|
||||
for (INode node : iterable.reverse()) {
|
||||
tokens.add(node.getText());
|
||||
}
|
||||
|
||||
Assert.assertEquals("gamma", tokens.get(0));
|
||||
Assert.assertEquals("beta", tokens.get(1));
|
||||
Assert.assertEquals("alpha", tokens.get(2));
|
||||
}
|
||||
|
||||
@Test(expected=NullPointerException.class)
|
||||
public void testStartWithNullThrowsNPE() {
|
||||
NodeIterable iterable = new NodeIterable(null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2020 itemis AG (http://www.itemis.eu) 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
|
||||
*******************************************************************************/
|
||||
package org.eclipse.xtext.nodemodel.util;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import org.eclipse.emf.ecore.EObject;
|
||||
import org.eclipse.emf.ecore.impl.EObjectImpl;
|
||||
import org.eclipse.xtext.nodemodel.ICompositeNode;
|
||||
import org.eclipse.xtext.nodemodel.ILeafNode;
|
||||
import org.eclipse.xtext.nodemodel.INode;
|
||||
import org.eclipse.xtext.nodemodel.impl.NodeModelBuilder;
|
||||
import org.eclipse.xtext.nodemodel.impl.RootNode;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Robert Lewis - Initial contribution and API
|
||||
*/
|
||||
public class NodeIteratorTest {
|
||||
|
||||
private static INode getSingleNode() {
|
||||
return new RootNode();
|
||||
}
|
||||
|
||||
public static INode nodeWithTwoSiblings() {
|
||||
NodeModelBuilder builder = new NodeModelBuilder();
|
||||
String text = "alpha beta gamma";
|
||||
|
||||
ICompositeNode root = builder.newRootNode(text);
|
||||
|
||||
EObject alpha = new EObjectImpl() {};
|
||||
ILeafNode alphaNode = builder.newLeafNode(text.indexOf("alpha"), "alpha".length(), alpha, false, null, root);
|
||||
|
||||
EObject beta = new EObjectImpl() {};
|
||||
ILeafNode betaNode = builder.newLeafNode(text.indexOf("beta"), "beta".length(), beta, false, null, root);
|
||||
|
||||
EObject gamma = new EObjectImpl() {};
|
||||
ILeafNode gammaNode = builder.newLeafNode(text.indexOf("gamma"), "gamma".length(), gamma, false, null, root);
|
||||
|
||||
return alphaNode;
|
||||
}
|
||||
|
||||
@Test(expected=NullPointerException.class)
|
||||
public void testStartWithNullThrowsNPE() {
|
||||
NodeIterator it = new NodeIterator(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleNodeHasNext() {
|
||||
INode single = getSingleNode();
|
||||
NodeIterator it = new NodeIterator(single);
|
||||
|
||||
Assert.assertTrue(it.hasNext());
|
||||
it.next();
|
||||
|
||||
Assert.assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test(expected=NoSuchElementException.class)
|
||||
public void testNextTooFarThrowsException() {
|
||||
INode single = getSingleNode();
|
||||
NodeIterator it = new NodeIterator(single);
|
||||
|
||||
it.next();
|
||||
|
||||
it.next();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterateThreeNodes() {
|
||||
INode alpha = nodeWithTwoSiblings();
|
||||
NodeIterator it = new NodeIterator(alpha);
|
||||
|
||||
Assert.assertTrue(it.hasNext());
|
||||
it.next();
|
||||
|
||||
Assert.assertTrue(it.hasNext());
|
||||
it.next();
|
||||
|
||||
Assert.assertTrue(it.hasNext());
|
||||
it.next();
|
||||
|
||||
Assert.assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleNodeHasPrevious() {
|
||||
INode single = getSingleNode();
|
||||
NodeIterator it = new NodeIterator(single);
|
||||
|
||||
Assert.assertTrue(it.hasPrevious());
|
||||
it.previous();
|
||||
|
||||
Assert.assertFalse(it.hasPrevious());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIteratePreviousStartsWithLastNode() {
|
||||
INode alpha = nodeWithTwoSiblings();
|
||||
NodeIterator it = new NodeIterator(alpha);
|
||||
|
||||
INode result = it.previous();
|
||||
|
||||
Assert.assertEquals("gamma", result.getText());
|
||||
}
|
||||
|
||||
@Test(expected=NoSuchElementException.class)
|
||||
public void testPreviousTooFarThrowsException() {
|
||||
INode single = getSingleNode();
|
||||
NodeIterator it = new NodeIterator(single);
|
||||
|
||||
it.previous();
|
||||
|
||||
it.previous();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIterateThreeNodesInReverse() {
|
||||
INode alpha = nodeWithTwoSiblings();
|
||||
NodeIterator it = new NodeIterator(alpha);
|
||||
|
||||
Assert.assertTrue(it.hasPrevious());
|
||||
it.previous();
|
||||
|
||||
Assert.assertTrue(it.hasPrevious());
|
||||
it.previous();
|
||||
|
||||
Assert.assertTrue(it.hasPrevious());
|
||||
it.previous();
|
||||
|
||||
Assert.assertFalse(it.hasPrevious());
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package org.eclipse.xtext.nodemodel.impl;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.xtext.nodemodel.BidiIterable;
|
||||
import org.eclipse.xtext.nodemodel.BidiIterator;
|
||||
import org.eclipse.xtext.nodemodel.util.ReversedBidiIterator;
|
||||
|
@ -12,6 +14,7 @@ public class BasicNodeIterable implements BidiIterable<AbstractNode> {
|
|||
private final AbstractNode startWith;
|
||||
|
||||
protected BasicNodeIterable(AbstractNode startWith) {
|
||||
Objects.requireNonNull(startWith);
|
||||
this.startWith = startWith;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
package org.eclipse.xtext.nodemodel.impl;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.xtext.nodemodel.BidiIterator;
|
||||
|
||||
|
@ -23,6 +24,7 @@ public class BasicNodeIterator extends UnmodifiableIterator<AbstractNode> implem
|
|||
private AbstractNode lastReturned;
|
||||
|
||||
protected BasicNodeIterator(AbstractNode startWith) {
|
||||
Objects.requireNonNull(startWith);
|
||||
this.startWith = startWith;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.eclipse.xtext.nodemodel.util;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.xtext.nodemodel.BidiIterable;
|
||||
import org.eclipse.xtext.nodemodel.BidiIterator;
|
||||
import org.eclipse.xtext.nodemodel.INode;
|
||||
|
@ -12,6 +14,7 @@ public class NodeIterable implements BidiIterable<INode> {
|
|||
private final INode startWith;
|
||||
|
||||
public NodeIterable(INode startWith) {
|
||||
Objects.requireNonNull(startWith);
|
||||
this.startWith = startWith;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
package org.eclipse.xtext.nodemodel.util;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.xtext.nodemodel.BidiIterator;
|
||||
import org.eclipse.xtext.nodemodel.INode;
|
||||
|
@ -23,10 +24,7 @@ public class NodeIterator extends UnmodifiableIterator<INode> implements BidiIte
|
|||
private INode lastReturned;
|
||||
|
||||
public NodeIterator(INode startWith) {
|
||||
if (startWith == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
Objects.requireNonNull(startWith);
|
||||
this.startWith = startWith;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue