From a3b4b45e4e381e604f58744862458fc4f13bae38 Mon Sep 17 00:00:00 2001 From: Tom Morris Date: Sun, 23 Jun 2013 12:04:48 -0400 Subject: [PATCH] Support non-string types in facetCount() - fixes #591 --- .../util/ExpressionNominalValueGrouper.java | 5 +- .../tests/expr/functions/FunctionTests.java | 143 ++++++++++++++++++ 2 files changed, 146 insertions(+), 2 deletions(-) create mode 100644 main/tests/server/src/com/google/refine/tests/expr/functions/FunctionTests.java diff --git a/main/src/com/google/refine/browsing/util/ExpressionNominalValueGrouper.java b/main/src/com/google/refine/browsing/util/ExpressionNominalValueGrouper.java index 4cec02feb..a0039d7ce 100644 --- a/main/src/com/google/refine/browsing/util/ExpressionNominalValueGrouper.java +++ b/main/src/com/google/refine/browsing/util/ExpressionNominalValueGrouper.java @@ -50,6 +50,7 @@ import com.google.refine.model.Cell; import com.google.refine.model.Project; import com.google.refine.model.Record; import com.google.refine.model.Row; +import com.google.refine.util.StringUtils; /** * Visit matched rows or records and group them into facet choices based on the values computed @@ -174,7 +175,7 @@ public class ExpressionNominalValueGrouper implements RowVisitor, RecordVisitor if (ExpressionUtils.isError(value)) { hasError = true; } else if (ExpressionUtils.isNonBlankData(value)) { - String valueString = value.toString(); + String valueString = StringUtils.toString(value); IndexedNominalFacetChoice facetChoice = choices.get(valueString); if (facetChoice != null) { @@ -236,7 +237,7 @@ public class ExpressionNominalValueGrouper implements RowVisitor, RecordVisitor if (ExpressionUtils.isError(choiceValue)) { return errorCount; } else if (ExpressionUtils.isNonBlankData(choiceValue)) { - IndexedNominalFacetChoice choice = choices.get(choiceValue); + IndexedNominalFacetChoice choice = choices.get(StringUtils.toString(choiceValue)); return choice != null ? choice.count : 0; } else { return blankCount; diff --git a/main/tests/server/src/com/google/refine/tests/expr/functions/FunctionTests.java b/main/tests/server/src/com/google/refine/tests/expr/functions/FunctionTests.java new file mode 100644 index 000000000..2a0d4f92f --- /dev/null +++ b/main/tests/server/src/com/google/refine/tests/expr/functions/FunctionTests.java @@ -0,0 +1,143 @@ +/* + +Copyright 2013, Thomas F. Morris +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + */ + +package com.google.refine.tests.expr.functions; + +import static org.mockito.Mockito.mock; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Properties; + +import org.json.JSONObject; +import org.slf4j.LoggerFactory; +import org.testng.Assert; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import com.google.refine.ProjectManager; +import com.google.refine.ProjectMetadata; +import com.google.refine.browsing.Engine; +import com.google.refine.expr.EvalError; +import com.google.refine.grel.ControlFunctionRegistry; +import com.google.refine.grel.Function; +import com.google.refine.io.FileProjectManager; +import com.google.refine.model.Cell; +import com.google.refine.model.Column; +import com.google.refine.model.ModelException; +import com.google.refine.model.Project; +import com.google.refine.model.Row; +import com.google.refine.tests.RefineTest; + + +public class FunctionTests extends RefineTest { + + static Properties bindings; + Project project; + Properties options; + JSONObject engine_config; + Engine engine; + + + @Override + @BeforeTest + public void init() { + logger = LoggerFactory.getLogger(this.getClass()); + } + + @BeforeMethod + public void SetUp() throws IOException, ModelException { + bindings = new Properties(); + + Path dir = Files.createTempDirectory("openrefine-test-workspace-dir"); + FileProjectManager.initialize(dir.toFile()); + project = new Project(); + ProjectMetadata pm = new ProjectMetadata(); + pm.setName("TNG Test Project"); + ProjectManager.singleton.registerProject(project, pm); + + int index = project.columnModel.allocateNewCellIndex(); + Column column = new Column(index,"Column A"); + project.columnModel.addColumn(index, column, true); + + options = mock(Properties.class); + + bindings.put("project", project); + + // Five rows of a's and five of 1s + for (int i = 0; i < 10; i++) { + Row row = new Row(1); + row.setCell(0, new Cell(i < 5 ? "a":new Integer(1), null)); + project.rows.add(row); + } + } + + @AfterMethod + public void TearDown() { + bindings = null; + } + + /** + * Lookup a control function by name and invoke it with a variable number of args + */ + private static Object invoke(String name,Object... args) { + // registry uses static initializer, so no need to set it up + Function function = ControlFunctionRegistry.getFunction(name); + if (function == null) { + throw new IllegalArgumentException("Unknown function "+name); + } + if (args == null) { + return function.call(bindings,new Object[0]); + } else { + return function.call(bindings,args); + } + } + + @Test + public void testInvalidParams() { + Assert.assertTrue(invoke("facetCount") instanceof EvalError); + Assert.assertTrue(invoke("facetCount", "one","two","three") instanceof EvalError); + Assert.assertTrue(invoke("facetCount", "one","bad(","Column A") instanceof EvalError); + + } + + @Test + public void testFacetCount() { + Assert.assertEquals(invoke("facetCount", "a", "value", "Column A"),Integer.valueOf(5)); + Assert.assertEquals(invoke("facetCount", new Integer(1), "value", "Column A"),Integer.valueOf(5)); + Assert.assertEquals(invoke("facetCount", new Integer(2), "value+1", "Column A"),Integer.valueOf(5)); + } +}