Skip to content

Commit

Permalink
Use matcher for nodes
Browse files Browse the repository at this point in the history
  • Loading branch information
Mats-SX committed Mar 3, 2016
1 parent 9e9c496 commit 342cdf0
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,18 @@
import cypher.feature.parser.matchers.IntegerMatcher;
import cypher.feature.parser.matchers.ListMatcher;
import cypher.feature.parser.matchers.MapMatcher;
import cypher.feature.parser.matchers.NodeMatcher;
import cypher.feature.parser.matchers.NullMatcher;
import cypher.feature.parser.matchers.StringMatcher;
import cypher.feature.parser.matchers.ValueMatcher;

import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;

import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.RelationshipType;
Expand Down Expand Up @@ -171,26 +174,14 @@ public void enterLabelName( FeatureResultsParser.LabelNameContext ctx )
@Override
public void exitNode( FeatureResultsParser.NodeContext ctx )
{
final Map<String,Object> properties = getMapOrEmpty();
final ArrayList<Label> nodeLabels = new ArrayList<>();
MapMatcher properties = getMapMatcher();

Set<String> labelNames = new HashSet<>();
while ( !names.isEmpty() )
{
nodeLabels.add( Label.label( names.pop() ) );
labelNames.add( names.pop() );
}
oldworkload.push( new ParsedNode()
{
@Override
public Map<String,Object> getAllProperties()
{
return properties;
}

@Override
public Iterable<Label> getLabels()
{
return nodeLabels;
}
} );
workload.push( new NodeMatcher( labelNames, properties ) );
}

private Map<String,Object> getMapOrEmpty()
Expand All @@ -205,6 +196,18 @@ private Map<String,Object> getMapOrEmpty()
}
}

private MapMatcher getMapMatcher()
{
if ( workload.isEmpty() )
{
return MapMatcher.EMPTY;
}
else
{
return (MapMatcher) workload.pop();
}
}

@Override
public void enterRelationshipTypeName( FeatureResultsParser.RelationshipTypeNameContext ctx )
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,20 @@
*/
package cypher.feature.parser.matchers;

import java.util.Collections;
import java.util.Map;
import java.util.Set;

public class MapMatcher implements ValueMatcher
{
public static final MapMatcher EMPTY = new MapMatcher( Collections.emptyMap() ) {
@Override
public String toString()
{
return "{ MapMatcher matching the empty map }";
}
};

private final Map<String,ValueMatcher> map;

public MapMatcher( Map<String,ValueMatcher> map )
Expand Down Expand Up @@ -52,4 +61,10 @@ public boolean matches( Object value )
}
return false;
}

@Override
public String toString()
{
return "{ MapMatcher matching a map with keys " + map.keySet().toString() + " and values " + map.values().toString() + " }";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2002-2016 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cypher.feature.parser.matchers;

import java.util.Set;

import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;

public class NodeMatcher implements ValueMatcher
{
private final Set<String> labelNames;
private final MapMatcher propertyMatcher;

public NodeMatcher( Set<String> labelNames, MapMatcher properties )
{
this.labelNames = labelNames;
this.propertyMatcher = properties;
}

@Override
public boolean matches( Object value )
{
if ( value instanceof Node )
{
Node node = (Node) value;
Iterable<Label> labels = node.getLabels();
boolean match = true;
int nbrOfLabels = 0;
for ( Label l : labels )
{
match &= labelNames.contains( l.name() );
++nbrOfLabels;
}
match &= nbrOfLabels == labelNames.size();

match &= propertyMatcher.matches( node.getAllProperties() );

return match;
}
return false;
}

@Override
public String toString()
{
return "{ NodeMatcher for a node with labelNames: " + labelNames.toString() + " and properties: " +
propertyMatcher.toString() + " }";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import java.util.Arrays.asList
import java.util.Collections.{emptyList, emptyMap}
import java.{lang, util}

import org.mockito.Mockito._
import org.mockito.Mockito.{when, mock, _}

import cypher.feature.parser.matchers.ValueMatcher
import org.neo4j.graphdb._
import org.scalatest.matchers.{MatchResult, Matcher}
Expand Down Expand Up @@ -115,22 +118,22 @@ class expectedResultsParserTest extends FunSuite with Matchers {
}

test("should parse nodes with labels") {
valueParse("()") should equal(parsedNode())
valueParse("(:T)") should equal(parsedNode(Seq("T")))
valueParse("(:T:T2:longlabel)") should equal(parsedNode(Seq("T", "T2", "longlabel")))
parse("()") should accept(mockedNode())
parse("(:T)") should accept(mockedNode(Seq("T")))
parse("(:T:T2:longlabel)") should accept(mockedNode(Seq("T", "T2", "longlabel")))
}

test("should parse nodes with properties") {
valueParse("({key:'value'})") should equal(parsedNode(properties = Map("key" -> "value")))
valueParse("({key:0})") should equal(parsedNode(properties = Map("key" -> Long.valueOf(0L))))
valueParse("({key:null, key2:[]})") should equal(parsedNode(properties = Map("key" -> null, "key2" -> emptyList())))
parse("({key:'value'})") should accept(mockedNode(properties = Map("key" -> "value")))
parse("({key:0})") should accept(mockedNode(properties = Map("key" -> Long.valueOf(0L))))
parse("({key:null, key2:[]})") should accept(mockedNode(properties = Map("key" -> null, "key2" -> emptyList())))
}

test("should parse nodes with labels and properties") {
valueParse("(:T {k:[]})") should equal(parsedNode(Seq("T"), Map("k" -> emptyList())))
val expected = parsedNode(Seq("T", "longlabel"),
parse("(:T {k:[]})") should accept(mockedNode(Seq("T"), Map("k" -> emptyList())))
val expected = mockedNode(Seq("T", "longlabel"),
Map("k" -> emptyList(), "verylongkeywithonlyletters" -> lang.Double.valueOf("Infinity")))
valueParse("(:T:longlabel {k:[], verylongkeywithonlyletters:Inf})") should equal(expected)
parse("(:T:longlabel {k:[], verylongkeywithonlyletters:Inf})") should accept(expected)
}

test("should parse relationships") {
Expand Down Expand Up @@ -185,6 +188,13 @@ class expectedResultsParserTest extends FunSuite with Matchers {
ParsedNode.parsedNode(list, asMap(properties))
}

private def mockedNode(labels: Seq[String] = Seq.empty, properties: Map[String, AnyRef] = Map.empty): Node = {
val node = mock(classOf[Node])
when(node.getLabels).thenReturn(labels.map(Label.label).toIterable.asJava)
when(node.getAllProperties).thenReturn(properties.asJava)
node
}

private def parsedPath(nodes: Seq[Node] = Seq.empty, rels: Seq[Relationship] = Seq.empty): Path = {
// ParsedPath.parsedPath(nodes.toIterable.asJava, rels.toIterable.asJava)
???
Expand All @@ -198,7 +208,6 @@ class expectedResultsParserTest extends FunSuite with Matchers {
map
}


case class accept(expected: Any)
extends Matcher[ValueMatcher] {

Expand Down

0 comments on commit 342cdf0

Please sign in to comment.