001/* 002 * Copyright (c) 2007-2022 The Cascading Authors. All Rights Reserved. 003 * 004 * Project and contact information: https://cascading.wensel.net/ 005 * 006 * This file is part of the Cascading project. 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020 021package cascading.local.tap.neo4j; 022 023import java.util.LinkedHashMap; 024import java.util.LinkedHashSet; 025import java.util.Map; 026import java.util.Objects; 027import java.util.Set; 028import java.util.function.Function; 029import java.util.stream.Collectors; 030 031import com.fasterxml.jackson.annotation.JsonCreator; 032import com.fasterxml.jackson.annotation.JsonGetter; 033import com.fasterxml.jackson.annotation.JsonIgnore; 034import com.fasterxml.jackson.annotation.JsonInclude; 035import com.fasterxml.jackson.annotation.JsonProperty; 036import com.fasterxml.jackson.annotation.JsonSetter; 037import com.fasterxml.jackson.core.JsonPointer; 038import com.fasterxml.jackson.databind.JsonNode; 039import heretical.pointer.path.Pointer; 040 041import static heretical.pointer.path.json.JSONNestedPointerCompiler.COMPILER; 042 043/** 044 * 045 */ 046public class JSONGraphSpec implements GraphSpec 047 { 048 public static class Ref 049 { 050 @JsonInclude(JsonInclude.Include.NON_NULL) 051 String fromPointer; 052 @JsonInclude(JsonInclude.Include.NON_NULL) 053 Object defaultValue; 054 @JsonIgnore 055 Function<JsonNode, Object> function; 056 057 public Ref( Function<JsonNode, Object> function ) 058 { 059 this.function = function; 060 } 061 062 public Ref( Object defaultValue ) 063 { 064 this( null, defaultValue ); 065 } 066 067 @JsonCreator 068 public Ref( @JsonProperty("fromPointer") String fromPointer, @JsonProperty("defaultValue") Object defaultValue ) 069 { 070 this.fromPointer = fromPointer; 071 this.defaultValue = defaultValue; 072 073 if( fromPointer != null ) 074 { 075 Pointer<JsonNode> pointer = COMPILER.compile( fromPointer ); 076 function = n -> pointer.at( n ) == null ? defaultValue : JSONUtil.at( pointer, n ); 077 } 078 else 079 { 080 function = n -> defaultValue; 081 } 082 } 083 084 public String getFromPointer() 085 { 086 return fromPointer; 087 } 088 089 public Object getDefaultValue() 090 { 091 return defaultValue; 092 } 093 094 public Function<JsonNode, Object> getFunction() 095 { 096 return function; 097 } 098 099 @Override 100 public boolean equals( Object o ) 101 { 102 if( this == o ) 103 return true; 104 if( !( o instanceof Ref ) ) 105 return false; 106 Ref ref = (Ref) o; 107 return Objects.equals( fromPointer, ref.fromPointer ) && 108 Objects.equals( defaultValue, ref.defaultValue ) && 109 Objects.equals( function, ref.function ); 110 } 111 112 @Override 113 public int hashCode() 114 { 115 return Objects.hash( fromPointer, defaultValue, function ); 116 } 117 } 118 119 public static class EdgeSpec 120 { 121 String edgeType; 122 String targetLabel; 123 Map<String, Ref> targetProperties = new LinkedHashMap<>(); // used for match or create 124 125 public EdgeSpec() 126 { 127 } 128 129 public EdgeSpec( String edgeType ) 130 { 131 this.edgeType = edgeType; 132 } 133 134 @JsonCreator 135 public EdgeSpec( @JsonProperty("edgeType") String edgeType, @JsonProperty("targetLabel") String targetLabel ) 136 { 137 this( edgeType ); 138 this.targetLabel = targetLabel; 139 } 140 141 public boolean hasEdgeType() 142 { 143 return getEdgeType() != null; 144 } 145 146 public String getEdgeType() 147 { 148 return edgeType; 149 } 150 151 public boolean hasTargetLabel() 152 { 153 return getTargetLabel() != null; 154 } 155 156 public String getTargetLabel() 157 { 158 return targetLabel; 159 } 160 161 public boolean hasTargetProperties() 162 { 163 return !targetProperties.isEmpty(); 164 } 165 166 @JsonGetter("targetProperties") 167 public Map<String, Ref> getRefTargetProperties() 168 { 169 return targetProperties; 170 } 171 172 public Map<String, Function<JsonNode, Object>> getTargetProperties() 173 { 174 return targetProperties.entrySet().stream().collect( Collectors.toMap( Map.Entry::getKey, e -> e.getValue().function ) ); 175 } 176 177 public EdgeSpec addTargetLabel( String targetLabel ) 178 { 179 this.targetLabel = targetLabel; 180 181 return this; 182 } 183 184 public EdgeSpec addTargetProperty( String property, Object value ) 185 { 186 return addTargetProperty( property, new Ref( value ) ); 187 } 188 189 public EdgeSpec addTargetProperty( String property, String fromPointer, Object defaultValue ) 190 { 191 return addTargetProperty( property, new Ref( fromPointer, defaultValue ) ); 192 } 193 194 public EdgeSpec addTargetProperty( String property, Function<JsonNode, Object> function ) 195 { 196 return addTargetProperty( property, new Ref( function ) ); 197 } 198 199 @JsonSetter("targetProperties") 200 public EdgeSpec addTargetProperty( String property, Ref ref ) 201 { 202 if( targetProperties.put( property, ref ) != null ) 203 { 204 throw new IllegalArgumentException( "match property: " + property + ", already exists" ); 205 } 206 207 return this; 208 } 209 210 @Override 211 public boolean equals( Object o ) 212 { 213 if( this == o ) 214 return true; 215 if( !( o instanceof EdgeSpec ) ) 216 return false; 217 EdgeSpec edgeSpec = (EdgeSpec) o; 218 return Objects.equals( edgeType, edgeSpec.edgeType ) && 219 Objects.equals( targetLabel, edgeSpec.targetLabel ) && 220 Objects.equals( targetProperties, edgeSpec.targetProperties ); 221 } 222 223 @Override 224 public int hashCode() 225 { 226 return Objects.hash( edgeType, targetLabel, targetProperties ); 227 } 228 } 229 230 String nodeLabel; 231 Map<String, Ref> properties = new LinkedHashMap<>(); // used for match or create 232 Set<EdgeSpec> edges = new LinkedHashSet<>(); 233 String valuesPointer = null; // return root by default 234 Function<JsonNode, JsonNode> valueRef = n -> n; 235 236 @JsonCreator 237 public JSONGraphSpec( @JsonProperty("nodeLabel") String nodeLabel ) 238 { 239 this.nodeLabel = nodeLabel; 240 } 241 242 public String getNodeLabel() 243 { 244 return nodeLabel; 245 } 246 247 public boolean hasNodeLabel() 248 { 249 return getNodeLabel() != null; 250 } 251 252 @JsonGetter("properties") 253 public Map<String, Ref> getRefProperties() 254 { 255 return properties; 256 } 257 258 @JsonIgnore 259 public Map<String, Function<JsonNode, Object>> getProperties() 260 { 261 return properties.entrySet().stream().collect( Collectors.toMap( Map.Entry::getKey, e -> e.getValue().function ) ); 262 } 263 264 public boolean hasProperties() 265 { 266 return !properties.isEmpty(); 267 } 268 269 public JSONGraphSpec addProperty( String property, Object value ) 270 { 271 return addProperty( property, new Ref( value ) ); 272 } 273 274 public JSONGraphSpec addProperty( String property, String fromPointer, Object defaultValue ) 275 { 276 return addProperty( property, new Ref( fromPointer, defaultValue ) ); 277 } 278 279 public JSONGraphSpec addProperty( String property, Function<JsonNode, Object> function ) 280 { 281 return addProperty( property, new Ref( function ) ); 282 } 283 284 @JsonSetter("properties") 285 public void addProperties( Map<String, Ref> properties ) 286 { 287 this.properties.putAll( properties ); 288 } 289 290 public JSONGraphSpec addProperty( String property, Ref ref ) 291 { 292 if( properties.put( property, ref ) != null ) 293 throw new IllegalArgumentException( "match property: " + property + " already exists" ); 294 295 return this; 296 } 297 298 @JsonIgnore 299 public Function<JsonNode, JsonNode> getValuesPointer() 300 { 301 return valueRef; 302 } 303 304 @JsonGetter("valuesPointer") 305 public String getRefValuesPointer() 306 { 307 return valuesPointer; 308 } 309 310 @JsonSetter("valuesPointer") 311 public JSONGraphSpec setValuesPointer( String valuesPointer ) 312 { 313 this.valuesPointer = valuesPointer; 314 this.valueRef = n -> n.at( JsonPointer.compile( valuesPointer ) ); 315 316 return this; 317 } 318 319 @JsonGetter("edges") 320 public Set<EdgeSpec> getEdges() 321 { 322 return edges; 323 } 324 325 public boolean hasEdges() 326 { 327 return !getEdges().isEmpty(); 328 } 329 330 public EdgeSpec addEdge() 331 { 332 return addEdge( (String) null ); 333 } 334 335 public EdgeSpec addEdge( String edgeType ) 336 { 337 return addEdge( new EdgeSpec( edgeType ) ); 338 } 339 340 public EdgeSpec addEdge( EdgeSpec edgeSpec ) 341 { 342 edges.add( edgeSpec ); 343 344 return edgeSpec; 345 } 346 347 @JsonSetter("edges") 348 public void setEdges( Set<EdgeSpec> edges ) 349 { 350 this.edges.addAll( edges ); 351 } 352 353 @Override 354 public boolean equals( Object o ) 355 { 356 if( this == o ) 357 return true; 358 if( !( o instanceof JSONGraphSpec ) ) 359 return false; 360 JSONGraphSpec graphSpec = (JSONGraphSpec) o; 361 return Objects.equals( nodeLabel, graphSpec.nodeLabel ) && 362 Objects.equals( properties, graphSpec.properties ) && 363 Objects.equals( edges, graphSpec.edges ) && 364 Objects.equals( valuesPointer, graphSpec.valuesPointer ); 365 } 366 367 @Override 368 public int hashCode() 369 { 370 return Objects.hash( nodeLabel, properties, edges, valuesPointer ); 371 } 372 }