Skip to content

Commit bae69bc

Browse files
Introduce an interface for Property. [improvement]
This change brings symmetry to the three things that make up a graph - Nodes - Relationships - Properties This change spares us from a further ABI incompatible change after 2021.1.0.
1 parent 7944741 commit bae69bc

File tree

7 files changed

+171
-125
lines changed

7 files changed

+171
-125
lines changed

neo4j-cypher-dsl/src/main/java/org/neo4j/cypherdsl/core/AbstractPropertyContainer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ public final Property property(String name) {
4040

4141
@Override
4242
public final Property property(String... names) {
43-
return Property.create(this, names);
43+
return InternalPropertyImpl.create(this, names);
4444
}
4545

4646
@Override
4747
public final Property property(Expression lookup) {
48-
return Property.create(this, lookup);
48+
return InternalPropertyImpl.create(this, lookup);
4949
}
5050

5151
@Override

neo4j-cypher-dsl/src/main/java/org/neo4j/cypherdsl/core/Cypher.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public static Property property(String containerName, String... names) {
140140
* @return A new property.
141141
*/
142142
public static Property property(Expression expression, String... names) {
143-
return Property.create(expression, names);
143+
return InternalPropertyImpl.create(expression, names);
144144
}
145145

146146
/**
@@ -166,7 +166,7 @@ public static Property property(String containerName, Expression lookup) {
166166
* @since 2021.0.0
167167
*/
168168
public static Property property(Expression expression, Expression lookup) {
169-
return Property.create(expression, lookup);
169+
return InternalPropertyImpl.create(expression, lookup);
170170
}
171171

172172
/**

neo4j-cypher-dsl/src/main/java/org/neo4j/cypherdsl/core/Expression.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,6 @@ default SortItem ascending() {
353353
*/
354354
default Property property(String... names) {
355355

356-
return Property.create(this, names);
356+
return InternalPropertyImpl.create(this, names);
357357
}
358358
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Copyright (c) 2019-2021 "Neo4j,"
3+
* Neo4j Sweden AB [https://neo4j.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* https://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.cypherdsl.core;
20+
21+
import static org.apiguardian.api.API.Status.INTERNAL;
22+
23+
import java.util.Arrays;
24+
import java.util.Collections;
25+
import java.util.List;
26+
import java.util.Optional;
27+
import java.util.stream.Collectors;
28+
29+
import org.apiguardian.api.API;
30+
import org.neo4j.cypherdsl.core.support.Visitor;
31+
import org.neo4j.cypherdsl.core.utils.Assertions;
32+
33+
@API(status = INTERNAL, since = "2021.1.0")
34+
final class InternalPropertyImpl implements Property {
35+
36+
static Property create(Named parentContainer, String... names) {
37+
38+
SymbolicName requiredSymbolicName = extractRequiredSymbolicName(parentContainer);
39+
return new InternalPropertyImpl(Optional.of(parentContainer), requiredSymbolicName, createListOfChainedNames(names), null);
40+
}
41+
42+
static Property create(Expression containerReference, String... names) {
43+
44+
Assertions.notNull(containerReference, "The property container is required.");
45+
return new InternalPropertyImpl(Optional.empty(), containerReference, createListOfChainedNames(names), null);
46+
}
47+
48+
static Property create(Named parentContainer, Expression lookup) {
49+
50+
SymbolicName requiredSymbolicName = extractRequiredSymbolicName(parentContainer);
51+
return new InternalPropertyImpl(Optional.of(parentContainer), requiredSymbolicName,
52+
Collections.singletonList(PropertyLookup.forExpression(lookup)), null);
53+
}
54+
55+
static Property create(Expression containerReference, Expression lookup) {
56+
57+
return new InternalPropertyImpl(Optional.empty(), containerReference,
58+
Collections.singletonList(PropertyLookup.forExpression(lookup)), null);
59+
}
60+
61+
/**
62+
* The reference to the container itself is optional.
63+
*/
64+
private final Named container;
65+
66+
/**
67+
* The expression pointing to the {@link #container} above is not.
68+
*/
69+
private final Expression containerReference;
70+
71+
/**
72+
* The name of this property.
73+
*/
74+
private final List<PropertyLookup> names;
75+
76+
/**
77+
* An optional, external (as in external to the graph) reference.
78+
*/
79+
private final String externalReference;
80+
81+
InternalPropertyImpl(Optional<Named> container, Expression containerReference, List<PropertyLookup> names,
82+
String externalReference) {
83+
84+
this.container = container.orElse(null);
85+
this.containerReference = containerReference;
86+
this.names = names;
87+
this.externalReference = externalReference;
88+
}
89+
90+
@Override
91+
public List<PropertyLookup> getNames() {
92+
return names;
93+
}
94+
95+
@Override
96+
public Named getContainer() {
97+
return container;
98+
}
99+
100+
@Override
101+
public String getName() {
102+
return externalReference != null ?
103+
this.externalReference :
104+
this.names.stream().map(PropertyLookup::getPropertyKeyName)
105+
.map(SymbolicName::getValue)
106+
.collect(Collectors.joining("."));
107+
}
108+
109+
@Override
110+
public Property referencedAs(String newReference) {
111+
112+
return new InternalPropertyImpl(Optional.ofNullable(this.container), containerReference, this.names,
113+
newReference);
114+
}
115+
116+
@Override
117+
public Operation to(Expression expression) {
118+
return Operations.set(this, expression);
119+
}
120+
121+
@Override
122+
public void accept(Visitor visitor) {
123+
visitor.enter(this);
124+
this.containerReference.accept(visitor);
125+
this.names.forEach(name -> name.accept(visitor));
126+
visitor.leave(this);
127+
}
128+
129+
private static List<PropertyLookup> createListOfChainedNames(String... names) {
130+
131+
Assertions.notEmpty(names, "The properties name is required.");
132+
133+
if (names.length == 1) {
134+
return Collections.singletonList(PropertyLookup.forName(names[0]));
135+
} else {
136+
return Arrays.stream(names).map(PropertyLookup::forName)
137+
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
138+
}
139+
}
140+
141+
private static SymbolicName extractRequiredSymbolicName(Named parentContainer) {
142+
try {
143+
return parentContainer.getRequiredSymbolicName();
144+
} catch (IllegalStateException e) {
145+
throw new IllegalArgumentException(
146+
"A property derived from a node or a relationship needs a parent with a symbolic name.");
147+
}
148+
}
149+
}

neo4j-cypher-dsl/src/main/java/org/neo4j/cypherdsl/core/Property.java

Lines changed: 12 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -19,131 +19,47 @@
1919
package org.neo4j.cypherdsl.core;
2020

2121
import static org.apiguardian.api.API.Status.EXPERIMENTAL;
22-
import static org.apiguardian.api.API.Status.INTERNAL;
2322

24-
import java.util.Arrays;
25-
import java.util.Collections;
2623
import java.util.List;
27-
import java.util.Optional;
28-
import java.util.stream.Collectors;
2924

3025
import org.apiguardian.api.API;
31-
import org.neo4j.cypherdsl.core.support.Visitor;
32-
import org.neo4j.cypherdsl.core.utils.Assertions;
3326

3427
/**
3528
* A property. A property might belong to a container such as a {@link Node} or {@link Relationship}, but it's not uncommon
3629
* to extract single properties from maps or from various datatypes such as a duration returned from stored procedures.
3730
* The container can be retrieved via {@link #getContainer()} in case the property belongs to a node or relationship.
3831
* <p>
39-
* A property has always a reference to the name of the object it was extracted from (see {@link #containerReference}.
32+
* A property has always a reference to the name of the object it was extracted from.
4033
*
4134
* @author Michael J. Simons
4235
* @since 1.0
4336
*/
4437
@API(status = EXPERIMENTAL, since = "1.0")
45-
public final class Property implements Expression {
46-
47-
static Property create(Named parentContainer, String... names) {
48-
49-
SymbolicName requiredSymbolicName = extractRequiredSymbolicName(parentContainer);
50-
return new Property(Optional.of(parentContainer), requiredSymbolicName, createListOfChainedNames(names), null);
51-
}
52-
53-
static Property create(Expression containerReference, String... names) {
54-
55-
Assertions.notNull(containerReference, "The property container is required.");
56-
return new Property(Optional.empty(), containerReference, createListOfChainedNames(names), null);
57-
}
58-
59-
static Property create(Named parentContainer, Expression lookup) {
60-
61-
SymbolicName requiredSymbolicName = extractRequiredSymbolicName(parentContainer);
62-
return new Property(Optional.of(parentContainer), requiredSymbolicName,
63-
Collections.singletonList(PropertyLookup.forExpression(lookup)), null);
64-
}
65-
66-
static Property create(Expression containerReference, Expression lookup) {
67-
68-
return new Property(Optional.empty(), containerReference,
69-
Collections.singletonList(PropertyLookup.forExpression(lookup)), null);
70-
}
71-
72-
private static List<PropertyLookup> createListOfChainedNames(String... names) {
73-
74-
Assertions.notEmpty(names, "The properties name is required.");
75-
76-
if (names.length == 1) {
77-
return Collections.singletonList(PropertyLookup.forName(names[0]));
78-
} else {
79-
return Arrays.stream(names).map(PropertyLookup::forName)
80-
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
81-
}
82-
}
83-
84-
/**
85-
* The reference to the container itself is optional.
86-
*/
87-
private final Named container;
38+
public interface Property extends Expression {
8839

8940
/**
90-
* The expression pointing to the {@link #container} above is not.
91-
*/
92-
private final Expression containerReference;
93-
94-
/**
95-
* The name of this property.
96-
*/
97-
private final List<PropertyLookup> names;
98-
99-
/**
100-
* An optional, external (as in external to the graph) reference.
41+
* Returns the concatenated names of the property or the external reference (See {@link #referencedAs(String)}) if set.
42+
*
43+
* @return A name to reference the property under in an external application
10144
*/
102-
private final String externalReference;
103-
104-
Property(Optional<Named> container, Expression containerReference, List<PropertyLookup> names, String externalReference) {
105-
106-
this.container = container.orElse(null);
107-
this.containerReference = containerReference;
108-
this.names = names;
109-
this.externalReference = externalReference;
110-
}
45+
@API(status = EXPERIMENTAL, since = "2021.1.0")
46+
String getName();
11147

11248
/**
11349
* @return The actual property being looked up. The order matters, so this will return a list, not a collection.
11450
*/
115-
@API(status = INTERNAL)
116-
List<PropertyLookup> getNames() {
117-
return names;
118-
}
51+
List<PropertyLookup> getNames();
11952

120-
@API(status = INTERNAL)
121-
Named getContainer() {
122-
return container;
123-
}
124-
125-
/**
126-
* Returns the concatenated names of the property or the {@link #externalReference} (See {@link #referencedAs(String)}) if set.
127-
*
128-
* @return A name to reference the property under in an external application
129-
*/
130-
@API(status = EXPERIMENTAL, since = "2021.1.0")
131-
public String getName() {
132-
return externalReference != null ? this.externalReference : this.names.stream().map(PropertyLookup::getPropertyKeyName)
133-
.map(SymbolicName::getValue)
134-
.collect(Collectors.joining("."));
135-
}
53+
Named getContainer();
13654

13755
/**
13856
* Creates a new property with an external reference.
57+
*
13958
* @param newReference An arbitrary, external reference
14059
* @return A new property
14160
*/
14261
@API(status = EXPERIMENTAL, since = "2021.1.0")
143-
public Property referencedAs(String newReference) {
144-
145-
return new Property(Optional.ofNullable(this.container), containerReference, this.names, newReference);
146-
}
62+
Property referencedAs(String newReference);
14763

14864
/**
14965
* Creates an {@link Operation} setting this property to a new value. The property does not track the operations
@@ -152,24 +68,5 @@ public Property referencedAs(String newReference) {
15268
* @param expression expression describing the new value
15369
* @return A new operation.
15470
*/
155-
public Operation to(Expression expression) {
156-
return Operations.set(this, expression);
157-
}
158-
159-
@Override
160-
public void accept(Visitor visitor) {
161-
visitor.enter(this);
162-
this.containerReference.accept(visitor);
163-
this.names.forEach(name -> name.accept(visitor));
164-
visitor.leave(this);
165-
}
166-
167-
private static SymbolicName extractRequiredSymbolicName(Named parentContainer) {
168-
try {
169-
return parentContainer.getRequiredSymbolicName();
170-
} catch (IllegalStateException e) {
171-
throw new IllegalArgumentException(
172-
"A property derived from a node or a relationship needs a parent with a symbolic name.");
173-
}
174-
}
71+
Operation to(Expression expression);
17572
}

neo4j-cypher-dsl/src/test/java/org/neo4j/cypherdsl/core/FunctionsIT.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ void manual() {
9696
.returning(
9797
Functions.reduce(n)
9898
.in(Functions.nodes(p))
99-
.map(totalAge.add(Property.create(n, "age")))
99+
.map(totalAge.add(Cypher.property(n, "age")))
100100
.accumulateOn(totalAge)
101101
.withInitialValueOf(Cypher.literalOf(0)).as("reduction")
102102
).build();

neo4j-cypher-dsl/src/test/java/org/neo4j/cypherdsl/core/PropertyTest.java renamed to neo4j-cypher-dsl/src/test/java/org/neo4j/cypherdsl/core/InternalPropertyImplTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,24 @@
2525
/**
2626
* @author Michael J. Simons
2727
*/
28-
class PropertyTest {
28+
class InternalPropertyImplTest {
2929

3030
@Test
3131
void simplePropertyGetNameShouldWork() {
3232

33-
assertThat(Property.create(SymbolicName.unresolved(), "simple").getName()).isEqualTo("simple");
33+
assertThat(InternalPropertyImpl.create(SymbolicName.unresolved(), "simple").getName()).isEqualTo("simple");
3434
}
3535

3636
@Test
3737
void multiplePropertyGetNameShouldWork() {
3838

39-
assertThat(Property.create(SymbolicName.unresolved(), "foo", "bar").getName()).isEqualTo("foo.bar");
39+
assertThat(InternalPropertyImpl.create(SymbolicName.unresolved(), "foo", "bar").getName()).isEqualTo("foo.bar");
4040
}
4141

4242
@Test
4343
void externalReferenceHasPriority() {
4444

45-
assertThat(Property.create(SymbolicName.unresolved(), "foo", "bar").referencedAs("something")
45+
assertThat(InternalPropertyImpl.create(SymbolicName.unresolved(), "foo", "bar").referencedAs("something")
4646
.getName()).isEqualTo("something");
4747
}
4848
}

0 commit comments

Comments
 (0)