From 129923bc9d54a330f74540a8ef3a826c86f60cdb Mon Sep 17 00:00:00 2001 From: Wayne Chan Date: Mon, 17 Jun 2013 18:34:20 -0400 Subject: [PATCH 1/2] Fixed a bug with replacing existing expression A CurrentModificationException is throw when modifying an existing expression on a cell. Separating the action of iterating through the Map and removing from the Map fixes it. --- src/main/java/org/boris/expr/util/Graph.java | 5 +- .../java/org/boris/expr/util/Graph.java.bak | 261 ++++++++++++++++++ .../org/boris/expr/DependencyEngineTest.java | 1 + .../boris/expr/DependencyEngineTest.java.bak | 104 +++++++ 4 files changed, 370 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/boris/expr/util/Graph.java.bak create mode 100644 src/test/java/org/boris/expr/DependencyEngineTest.java.bak diff --git a/src/main/java/org/boris/expr/util/Graph.java b/src/main/java/org/boris/expr/util/Graph.java index cc7b839..a5fe31a 100644 --- a/src/main/java/org/boris/expr/util/Graph.java +++ b/src/main/java/org/boris/expr/util/Graph.java @@ -56,8 +56,11 @@ public void clearInbounds(Object node) { Set s = (Set) inbounds.get(node); if (s != null) { Iterator i = s.iterator(); + Set remove = new HashSet(); while (i.hasNext()) - remove((Edge) i.next()); + remove.add((Edge) i.next()); + for (Edge edge : remove) + remove(edge); } } diff --git a/src/main/java/org/boris/expr/util/Graph.java.bak b/src/main/java/org/boris/expr/util/Graph.java.bak new file mode 100644 index 0000000..01784f2 --- /dev/null +++ b/src/main/java/org/boris/expr/util/Graph.java.bak @@ -0,0 +1,261 @@ +/******************************************************************************* + * This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Peter Smith + *******************************************************************************/ +package org.boris.expr.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class Graph implements Iterable +{ + private boolean wantsEdges = true; + private Set nodes = new HashSet(); + private Set edges = new HashSet(); + private Map outbounds = new HashMap(); + private Map inbounds = new HashMap(); + private List ordered = null; + private Set traversed = null; + + public void setIncludeEdges(boolean include) { + this.wantsEdges = include; + } + + public void add(Object node) { + nodes.add(node); + } + + public Set getInbounds(Object node) { + return (Set) inbounds.get(node); + } + + public Set getOutbounds(Object node) { + return (Set) outbounds.get(node); + } + + public void clearOutbounds(Object node) { + Set s = (Set) outbounds.get(node); + if (s != null) { + Iterator i = s.iterator(); + while (i.hasNext()) + remove((Edge) i.next()); + } + } + + public void clearInbounds(Object node) { + Set s = (Set) inbounds.get(node); + if (s != null) { + Iterator i = s.iterator(); + while (i.hasNext()) + remove((Edge) i.next()); + } + } + + public void remove(Object node) { + nodes.remove(node); + clearInbounds(node); + clearOutbounds(node); + } + + public void add(Edge e) throws GraphCycleException { + checkCycle(e); + nodes.add(e.source); + nodes.add(e.target); + edges.add(e); + Set in = (Set) inbounds.get(e.target); + if (in == null) + inbounds.put(e.target, in = new HashSet()); + in.add(e); + Set out = (Set) outbounds.get(e.source); + if (out == null) + outbounds.put(e.source, out = new HashSet()); + out.add(e); + } + + public void checkCycle(Edge e) throws GraphCycleException { + HashSet visited = new HashSet(); + visited.add(e.source); + checkCycle(e, visited); + } + + private void checkCycle(Edge e, HashSet visited) throws GraphCycleException { + if (visited.contains(e.target)) { + throw new GraphCycleException("Circular reference found: " + + e.source + " - " + e.target); + } + visited.add(e.target); + Set out = (Set) outbounds.get(e.target); + if (out != null) { + Iterator i = out.iterator(); + while (i.hasNext()) { + checkCycle((Edge) i.next(), visited); + } + } + } + + public void remove(Edge e) { + edges.remove(e); + Set in = (Set) inbounds.get(e.target); + if (in != null) + in.remove(e); + Set out = (Set) outbounds.get(e.source); + if (out != null) + out.remove(e); + } + + public void sort() { + ordered = new ArrayList(); + traversed = new HashSet(); + Iterator i = nodes.iterator(); + Set remains = new HashSet(nodes); + + // First traverse nodes without inbounds + while (i.hasNext()) { + Object o = i.next(); + Set in = (Set) inbounds.get(o); + if (in == null || in.isEmpty()) { + traverse(o); + remains.remove(o); + } + } + + // Now traverse the rest + i = remains.iterator(); + while (i.hasNext()) { + Object o = i.next(); + if (!traversed.contains(o)) { + traverse(o); + } + } + } + + private void traverse(Object node) { + Set in = (Set) inbounds.get(node); + if (in != null) { + Iterator i = in.iterator(); + + // if all inbounds haven't been traversed we must stop + while (i.hasNext()) { + Edge e = (Edge) i.next(); + if (!traversed.contains(e.source)) + return; + else if (wantsEdges) + ordered.add(e); + + } + } + + if (!traversed.contains(node)) { + traversed.add(node); + ordered.add(node); + } + + Set out = (Set) outbounds.get(node); + if (out == null || out.isEmpty()) { + return; + } + + Set avoid = new HashSet(); + + Iterator i = out.iterator(); + while (i.hasNext()) { + Edge e = (Edge) i.next(); + if (!traversed.contains(e)) { + if (traversed.contains(e.target)) { + avoid.add(e.target); + } + } + } + + i = out.iterator(); + while (i.hasNext()) { + Object n = ((Edge) i.next()).target; + if (!avoid.contains(n)) { + traverse(n); + } + } + } + + public void clear() { + edges.clear(); + inbounds.clear(); + outbounds.clear(); + nodes.clear(); + traversed = null; + ordered.clear(); + } + + public Iterator iterator() { + if (ordered == null) + sort(); + return ordered.iterator(); + } + + public void traverse(Object node, GraphTraversalListener listener) { + HashSet subgraph = new HashSet(); + walk(node, subgraph); + HashSet hs = new HashSet(); + hs.add(node); + traverse(node, listener, hs, subgraph); + } + + private void walk(Object node, Set traversed) { + traversed.add(node); + Set out = (Set) outbounds.get(node); + if (out != null) { + Iterator i = out.iterator(); + while (i.hasNext()) { + Edge e = (Edge) i.next(); + walk(e.target, traversed); + } + } + } + + private void traverse(Object node, GraphTraversalListener listener, + Set traversed, Set subgraph) { + Set edges = (Set) outbounds.get(node); + if (edges != null) { + Iterator i = edges.iterator(); + while (i.hasNext()) { + Edge e = (Edge) i.next(); + Set ins = (Set) inbounds.get(e.target); + Iterator j = ins.iterator(); + boolean traverse = true; + while (j.hasNext()) { + Edge in = (Edge) j.next(); + if (subgraph.contains(in.source) && + !traversed.contains(in.source) && + !node.equals(in.source)) { + traverse = false; + break; + } + } + if (traverse) { + listener.traverse(e.target); + traversed.add(e.target); + traverse(e.target, listener, traversed, subgraph); + } + } + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + Iterator i = edges.iterator(); + while (i.hasNext()) { + sb.append(i.next()); + sb.append("\n"); + } + return sb.toString(); + } +} diff --git a/src/test/java/org/boris/expr/DependencyEngineTest.java b/src/test/java/org/boris/expr/DependencyEngineTest.java index 36925a6..6fc5ee3 100644 --- a/src/test/java/org/boris/expr/DependencyEngineTest.java +++ b/src/test/java/org/boris/expr/DependencyEngineTest.java @@ -34,6 +34,7 @@ public void testRangeDependencies() throws Exception { e.set("B1", "2"); e.set("A2", "3"); e.set("B2", "4"); + e.set("D4", "=sum(A1:A2)"); e.set("D4", "=sum(A1:B2)"); assertResult(e, "D4", 10); e.set("A1", "10"); diff --git a/src/test/java/org/boris/expr/DependencyEngineTest.java.bak b/src/test/java/org/boris/expr/DependencyEngineTest.java.bak new file mode 100644 index 0000000..36925a6 --- /dev/null +++ b/src/test/java/org/boris/expr/DependencyEngineTest.java.bak @@ -0,0 +1,104 @@ +/******************************************************************************* + * This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Peter Smith + *******************************************************************************/ +package org.boris.expr; + +import junit.framework.TestCase; + +import org.boris.expr.engine.DependencyEngine; +import org.boris.expr.engine.Range; + +public class DependencyEngineTest extends TestCase +{ + public void testBasic() throws Exception { + DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); + e.set("B1", "=A1*2"); + e.set("A1", "=12*2"); + e.set("C1", "=B1*A1"); + assertResult(e, "B1", 48); + assertResult(e, "C1", 48 * 24); + e.set("A1", "2"); + assertResult(e, "B1", 4); + assertResult(e, "C1", 8); + } + + public void testRangeDependencies() throws Exception { + DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); + e.set("A1", "1"); + e.set("B1", "2"); + e.set("A2", "3"); + e.set("B2", "4"); + e.set("D4", "=sum(A1:B2)"); + assertResult(e, "D4", 10); + e.set("A1", "10"); + assertResult(e, "D4", 19); + } + + public void testFunction() throws Exception { + DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); + e.set("A4", "25"); + e.set("B6", "26"); + e.set("A1", "=sum(45,34,2.3,A4:B8)"); + assertResult(e, "A1", 132.3); + } + + public void testArrays() throws Exception { + DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); + e.set("A1:B2", "32"); + assertResult(e, "A2", 32); + e.set("D5:E6", "=TestRange(A1)"); + } + + public void testAliases() throws Exception { + DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); + e.setNamespace("Sheet1"); + e.addAlias("x", Range.valueOf("A1:B3")); + e.addAlias("alias1", Range.valueOf("A1")); + e.set("A1", "45"); + e.set("B1", "=alias1:x"); + e.set("B2", "=x"); + assertResult(e, "B1", 45); + assertResult(e, "B2", 45); + } + + public void testInvalidReference() throws Exception { + DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); + assertException(e, "B1:A1", "12"); + } + + public void testCircular() throws Exception { + DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); + assertException(e, "A1", "=A1"); + assertException(e, "A1", "=A1:B2"); + } + + public void testManualCalculate() throws Exception { + DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); + e.setAutoCalculate(false); + } + + private void assertException(DependencyEngine e, String range, String expression) + throws Exception { + try { + e.set(range, expression); + fail("Expected an exception"); + } catch (Exception ex) { + } + } + + private void assertResult(DependencyEngine e, String range, double value) + throws Exception { + Expr expr = e.getValue(Range.valueOf(range)); + if (expr instanceof ExprNumber) { + assertEquals(value, ((ExprNumber) expr).doubleValue()); + } else { + fail("Range: " + range + " does not contain a number"); + } + } +} \ No newline at end of file From 1f72f25043d5d3ad9f02504ea2a1d1fc0a74dddb Mon Sep 17 00:00:00 2001 From: Wayne Chan Date: Mon, 17 Jun 2013 18:41:25 -0400 Subject: [PATCH 2/2] Removing backup files Accidentally committed backup files. --- .../java/org/boris/expr/util/Graph.java.bak | 261 ------------------ .../boris/expr/DependencyEngineTest.java.bak | 104 ------- 2 files changed, 365 deletions(-) delete mode 100644 src/main/java/org/boris/expr/util/Graph.java.bak delete mode 100644 src/test/java/org/boris/expr/DependencyEngineTest.java.bak diff --git a/src/main/java/org/boris/expr/util/Graph.java.bak b/src/main/java/org/boris/expr/util/Graph.java.bak deleted file mode 100644 index 01784f2..0000000 --- a/src/main/java/org/boris/expr/util/Graph.java.bak +++ /dev/null @@ -1,261 +0,0 @@ -/******************************************************************************* - * This program and the accompanying materials - * are made available under the terms of the Common Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/cpl-v10.html - * - * Contributors: - * Peter Smith - *******************************************************************************/ -package org.boris.expr.util; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -public class Graph implements Iterable -{ - private boolean wantsEdges = true; - private Set nodes = new HashSet(); - private Set edges = new HashSet(); - private Map outbounds = new HashMap(); - private Map inbounds = new HashMap(); - private List ordered = null; - private Set traversed = null; - - public void setIncludeEdges(boolean include) { - this.wantsEdges = include; - } - - public void add(Object node) { - nodes.add(node); - } - - public Set getInbounds(Object node) { - return (Set) inbounds.get(node); - } - - public Set getOutbounds(Object node) { - return (Set) outbounds.get(node); - } - - public void clearOutbounds(Object node) { - Set s = (Set) outbounds.get(node); - if (s != null) { - Iterator i = s.iterator(); - while (i.hasNext()) - remove((Edge) i.next()); - } - } - - public void clearInbounds(Object node) { - Set s = (Set) inbounds.get(node); - if (s != null) { - Iterator i = s.iterator(); - while (i.hasNext()) - remove((Edge) i.next()); - } - } - - public void remove(Object node) { - nodes.remove(node); - clearInbounds(node); - clearOutbounds(node); - } - - public void add(Edge e) throws GraphCycleException { - checkCycle(e); - nodes.add(e.source); - nodes.add(e.target); - edges.add(e); - Set in = (Set) inbounds.get(e.target); - if (in == null) - inbounds.put(e.target, in = new HashSet()); - in.add(e); - Set out = (Set) outbounds.get(e.source); - if (out == null) - outbounds.put(e.source, out = new HashSet()); - out.add(e); - } - - public void checkCycle(Edge e) throws GraphCycleException { - HashSet visited = new HashSet(); - visited.add(e.source); - checkCycle(e, visited); - } - - private void checkCycle(Edge e, HashSet visited) throws GraphCycleException { - if (visited.contains(e.target)) { - throw new GraphCycleException("Circular reference found: " + - e.source + " - " + e.target); - } - visited.add(e.target); - Set out = (Set) outbounds.get(e.target); - if (out != null) { - Iterator i = out.iterator(); - while (i.hasNext()) { - checkCycle((Edge) i.next(), visited); - } - } - } - - public void remove(Edge e) { - edges.remove(e); - Set in = (Set) inbounds.get(e.target); - if (in != null) - in.remove(e); - Set out = (Set) outbounds.get(e.source); - if (out != null) - out.remove(e); - } - - public void sort() { - ordered = new ArrayList(); - traversed = new HashSet(); - Iterator i = nodes.iterator(); - Set remains = new HashSet(nodes); - - // First traverse nodes without inbounds - while (i.hasNext()) { - Object o = i.next(); - Set in = (Set) inbounds.get(o); - if (in == null || in.isEmpty()) { - traverse(o); - remains.remove(o); - } - } - - // Now traverse the rest - i = remains.iterator(); - while (i.hasNext()) { - Object o = i.next(); - if (!traversed.contains(o)) { - traverse(o); - } - } - } - - private void traverse(Object node) { - Set in = (Set) inbounds.get(node); - if (in != null) { - Iterator i = in.iterator(); - - // if all inbounds haven't been traversed we must stop - while (i.hasNext()) { - Edge e = (Edge) i.next(); - if (!traversed.contains(e.source)) - return; - else if (wantsEdges) - ordered.add(e); - - } - } - - if (!traversed.contains(node)) { - traversed.add(node); - ordered.add(node); - } - - Set out = (Set) outbounds.get(node); - if (out == null || out.isEmpty()) { - return; - } - - Set avoid = new HashSet(); - - Iterator i = out.iterator(); - while (i.hasNext()) { - Edge e = (Edge) i.next(); - if (!traversed.contains(e)) { - if (traversed.contains(e.target)) { - avoid.add(e.target); - } - } - } - - i = out.iterator(); - while (i.hasNext()) { - Object n = ((Edge) i.next()).target; - if (!avoid.contains(n)) { - traverse(n); - } - } - } - - public void clear() { - edges.clear(); - inbounds.clear(); - outbounds.clear(); - nodes.clear(); - traversed = null; - ordered.clear(); - } - - public Iterator iterator() { - if (ordered == null) - sort(); - return ordered.iterator(); - } - - public void traverse(Object node, GraphTraversalListener listener) { - HashSet subgraph = new HashSet(); - walk(node, subgraph); - HashSet hs = new HashSet(); - hs.add(node); - traverse(node, listener, hs, subgraph); - } - - private void walk(Object node, Set traversed) { - traversed.add(node); - Set out = (Set) outbounds.get(node); - if (out != null) { - Iterator i = out.iterator(); - while (i.hasNext()) { - Edge e = (Edge) i.next(); - walk(e.target, traversed); - } - } - } - - private void traverse(Object node, GraphTraversalListener listener, - Set traversed, Set subgraph) { - Set edges = (Set) outbounds.get(node); - if (edges != null) { - Iterator i = edges.iterator(); - while (i.hasNext()) { - Edge e = (Edge) i.next(); - Set ins = (Set) inbounds.get(e.target); - Iterator j = ins.iterator(); - boolean traverse = true; - while (j.hasNext()) { - Edge in = (Edge) j.next(); - if (subgraph.contains(in.source) && - !traversed.contains(in.source) && - !node.equals(in.source)) { - traverse = false; - break; - } - } - if (traverse) { - listener.traverse(e.target); - traversed.add(e.target); - traverse(e.target, listener, traversed, subgraph); - } - } - } - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - Iterator i = edges.iterator(); - while (i.hasNext()) { - sb.append(i.next()); - sb.append("\n"); - } - return sb.toString(); - } -} diff --git a/src/test/java/org/boris/expr/DependencyEngineTest.java.bak b/src/test/java/org/boris/expr/DependencyEngineTest.java.bak deleted file mode 100644 index 36925a6..0000000 --- a/src/test/java/org/boris/expr/DependencyEngineTest.java.bak +++ /dev/null @@ -1,104 +0,0 @@ -/******************************************************************************* - * This program and the accompanying materials - * are made available under the terms of the Common Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/cpl-v10.html - * - * Contributors: - * Peter Smith - *******************************************************************************/ -package org.boris.expr; - -import junit.framework.TestCase; - -import org.boris.expr.engine.DependencyEngine; -import org.boris.expr.engine.Range; - -public class DependencyEngineTest extends TestCase -{ - public void testBasic() throws Exception { - DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); - e.set("B1", "=A1*2"); - e.set("A1", "=12*2"); - e.set("C1", "=B1*A1"); - assertResult(e, "B1", 48); - assertResult(e, "C1", 48 * 24); - e.set("A1", "2"); - assertResult(e, "B1", 4); - assertResult(e, "C1", 8); - } - - public void testRangeDependencies() throws Exception { - DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); - e.set("A1", "1"); - e.set("B1", "2"); - e.set("A2", "3"); - e.set("B2", "4"); - e.set("D4", "=sum(A1:B2)"); - assertResult(e, "D4", 10); - e.set("A1", "10"); - assertResult(e, "D4", 19); - } - - public void testFunction() throws Exception { - DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); - e.set("A4", "25"); - e.set("B6", "26"); - e.set("A1", "=sum(45,34,2.3,A4:B8)"); - assertResult(e, "A1", 132.3); - } - - public void testArrays() throws Exception { - DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); - e.set("A1:B2", "32"); - assertResult(e, "A2", 32); - e.set("D5:E6", "=TestRange(A1)"); - } - - public void testAliases() throws Exception { - DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); - e.setNamespace("Sheet1"); - e.addAlias("x", Range.valueOf("A1:B3")); - e.addAlias("alias1", Range.valueOf("A1")); - e.set("A1", "45"); - e.set("B1", "=alias1:x"); - e.set("B2", "=x"); - assertResult(e, "B1", 45); - assertResult(e, "B2", 45); - } - - public void testInvalidReference() throws Exception { - DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); - assertException(e, "B1:A1", "12"); - } - - public void testCircular() throws Exception { - DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); - assertException(e, "A1", "=A1"); - assertException(e, "A1", "=A1:B2"); - } - - public void testManualCalculate() throws Exception { - DependencyEngine e = new DependencyEngine(new BasicEngineProvider()); - e.setAutoCalculate(false); - } - - private void assertException(DependencyEngine e, String range, String expression) - throws Exception { - try { - e.set(range, expression); - fail("Expected an exception"); - } catch (Exception ex) { - } - } - - private void assertResult(DependencyEngine e, String range, double value) - throws Exception { - Expr expr = e.getValue(Range.valueOf(range)); - if (expr instanceof ExprNumber) { - assertEquals(value, ((ExprNumber) expr).doubleValue()); - } else { - fail("Range: " + range + " does not contain a number"); - } - } -} \ No newline at end of file