2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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 * ============LICENSE_END=========================================================
20 * ECOMP is a trademark and service mark of AT&T Intellectual Property.
22 package org.onap.aai.serialization.db;
24 import static com.jayway.jsonpath.Criteria.where;
25 import static com.jayway.jsonpath.Filter.filter;
27 import java.io.InputStream;
29 import java.util.Map.Entry;
30 import java.util.concurrent.ConcurrentHashMap;
32 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
33 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
34 import org.apache.tinkerpop.gremlin.structure.Direction;
35 import org.apache.tinkerpop.gremlin.structure.Edge;
36 import org.apache.tinkerpop.gremlin.structure.Vertex;
37 import org.onap.aai.db.props.AAIProperties;
38 import org.onap.aai.exceptions.AAIException;
39 import org.onap.aai.introspection.Version;
40 import org.onap.aai.serialization.db.exceptions.EdgeMultiplicityException;
41 import org.onap.aai.serialization.db.exceptions.MultipleEdgeRuleFoundException;
42 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
44 import com.att.eelf.configuration.EELFLogger;
45 import com.att.eelf.configuration.EELFManager;
46 import com.google.common.collect.ArrayListMultimap;
47 import com.google.common.collect.Multimap;
48 import com.jayway.jsonpath.DocumentContext;
49 import com.jayway.jsonpath.Filter;
50 import com.jayway.jsonpath.JsonPath;
52 public class EdgeRules {
54 private static final String LABEL = "label";
56 private static final String NOT_DIRECTION_NOTATION = "!${direction}";
58 private static final String DIRECTION_NOTATION = "${direction}";
60 private EELFLogger logger = EELFManager.getInstance().getLogger(EdgeRules.class);
62 private DocumentContext rulesDoc;
65 * Loads the most recent DbEdgeRules json file for later parsing.
66 * Only need most recent version for actual A&AI operations that call this class;
67 * the old ones are only used in tests.
71 String json = this.getEdgeRuleJson(Version.getLatest());
72 rulesDoc = JsonPath.parse(json);
76 private EdgeRules(String rulesFilename) {
77 String json = this.getEdgeRuleJson(rulesFilename);
78 rulesDoc = JsonPath.parse(json);
82 * Loads the versioned DbEdgeRules json file for later parsing.
84 private EdgeRules(Version version) {
85 String json = this.getEdgeRuleJson(version);
86 rulesDoc = JsonPath.parse(json);
90 * Gets the single instance of EdgeRules.
92 * @return single instance of EdgeRules
94 public static EdgeRules getInstance() {
95 return Helper.INSTANCE;
100 * Gets the versioned instance of EdgeRules.
102 * @return versioned instance of EdgeRules
104 public static EdgeRules getInstance(Version v) {
105 return Helper.getVersionedEdgeRules(v);
110 * Loads edge rules from the given file.
112 * @param rulesFilename - name of the file to load rules from
113 * @return the EdgeRules instance
115 public static EdgeRules getInstance(String rulesFilename) {
116 return Helper.getEdgeRulesByFilename(rulesFilename);
119 private String getEdgeRuleJson(String rulesFilename) {
120 InputStream is = getClass().getResourceAsStream(rulesFilename);
122 Scanner scanner = new Scanner(is);
123 String json = scanner.useDelimiter("\\Z").next();
129 private String getEdgeRuleJson(Version version) {
130 return this.getEdgeRuleJson("/dbedgerules/DbEdgeRules_" + version.toString() + ".json");
134 * Adds the tree edge.
136 * @param aVertex the out vertex
137 * @param bVertex the in vertex
139 * @throws AAIException the AAI exception
141 public Edge addTreeEdge(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException {
142 return this.addEdge(EdgeType.TREE, traversalSource, aVertex, bVertex, false, null);
148 * @param aVertex the out vertex
149 * @param bVertex the in vertex
151 * @throws AAIException the AAI exception
153 public Edge addEdge(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException {
154 return this.addEdge(traversalSource, aVertex, bVertex, null);
157 public Edge addEdge(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
158 return this.addEdge(EdgeType.COUSIN, traversalSource, aVertex, bVertex, false, label);
162 * Adds the tree edge.
164 * @param aVertex the out vertex
165 * @param bVertex the in vertex
167 * @throws AAIException the AAI exception
169 public Edge addTreeEdgeIfPossible(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException {
170 return this.addEdge(EdgeType.TREE, traversalSource, aVertex, bVertex, true, null);
176 * @param aVertex the out vertex
177 * @param bVertex the in vertex
179 * @throws AAIException the AAI exception
181 public Edge addEdgeIfPossible(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException {
182 return this.addEdgeIfPossible(traversalSource, aVertex, bVertex, null);
185 public Edge addEdgeIfPossible(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
186 return this.addEdge(EdgeType.COUSIN, traversalSource, aVertex, bVertex, true, label);
192 * @param type the type
193 * @param aVertex the out vertex
194 * @param bVertex the in vertex
196 * @throws AAIException the AAI exception
198 private Edge addEdge(EdgeType type, GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex, boolean isBestEffort, String label) throws AAIException {
200 EdgeRule rule = this.getEdgeRule(type, aVertex, bVertex, label);
204 Optional<String> message = this.validateMultiplicity(rule, traversalSource, aVertex, bVertex);
206 if (message.isPresent() && !isBestEffort) {
207 throw new EdgeMultiplicityException(message.get());
209 if (!message.isPresent()) {
210 if (rule.getDirection().equals(Direction.OUT)) {
211 e = aVertex.addEdge(rule.getLabel(), bVertex);
212 } else if (rule.getDirection().equals(Direction.IN)) {
213 e = bVertex.addEdge(rule.getLabel(), aVertex);
216 this.addProperties(e, rule);
222 * Adds the properties.
224 * @param edge the edge
225 * @param rule the rule
227 public void addProperties(Edge edge, EdgeRule rule) {
229 // In DbEdgeRules.EdgeRules -- What we have as "edgeRule" is a comma-delimited set of strings.
230 // The first item is the edgeLabel.
231 // The second in the list is always "direction" which is always OUT for the way we've implemented it.
232 // Items starting at "firstTagIndex" and up are all assumed to be booleans that map according to
233 // tags as defined in EdgeInfoMap.
234 // Note - if they are tagged as 'reverse', that means they get the tag name with "-REV" on it
235 Map<EdgeProperty, String> propMap = rule.getEdgeProperties();
237 for (Entry<EdgeProperty, String> entry : propMap.entrySet()) {
238 edge.property(entry.getKey().toString(), entry.getValue());
241 edge.property(AAIProperties.AAI_UUID, UUID.randomUUID().toString());
245 * Checks if any edge rules exist between the two given node types, in either A|B or B|A order.
247 * @param nodeA - node at one end of the edge
248 * @param nodeB - node at the other end
249 * @return true, if any such rules exist
251 public boolean hasEdgeRule(String nodeA, String nodeB) {
252 return this.hasEdgeRule(nodeA, nodeB, null);
256 * Checks if any edge rules exist between the two given node types with contains-other-v !NONE, in either A|B or B|A order.
258 * @param nodeA - node at one end of the edge
259 * @param nodeB - node at the other end
260 * @return true, if any such rules exist
262 public boolean hasTreeEdgeRule(String nodeA, String nodeB) {
263 return this.hasEdgeRule(EdgeType.TREE, nodeA, nodeB, null);
267 * Checks if any edge rules exist between the two given node types with contains-other-v NONE, in either A|B or B|A order.
269 * @param nodeA - node at one end of the edge
270 * @param nodeB - node at the other end
271 * @param label - edge label
272 * @return true, if any such rules exist
274 public boolean hasCousinEdgeRule(String nodeA, String nodeB, String label) {
275 return this.hasEdgeRule(EdgeType.COUSIN, nodeA, nodeB, label);
279 * Checks if any edge rules exist between the two given nodes with contains-other-v !NONE, in either A|B or B|A order.
281 * @param aVertex - node at one end of the edge
282 * @param bVertex - node at the other end
283 * @return true, if any such rules exist
285 public boolean hasTreeEdgeRule(Vertex aVertex, Vertex bVertex) {
286 String outType = aVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
287 String inType = bVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
288 return this.hasTreeEdgeRule(outType, inType);
292 * Checks if any edge rules exist between the two given nodes with contains-other-v NONE with edge label, in either A|B or B|A order.
294 * @param aVertex - node at one end of the edge
295 * @param bVertex - node at the other end
296 * @param label - edge label
297 * @return true, if any such rules exist
299 public boolean hasCousinEdgeRule(Vertex aVertex, Vertex bVertex, String label) {
300 String outType = aVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
301 String inType = bVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
302 return this.hasCousinEdgeRule(outType, inType, label);
306 * Checks if any edge rules exist between the two given nodes w/ edge label, in either A|B or B|A order.
308 * @param nodeA - node at one end of the edge
309 * @param nodeB - node at the other end
310 * @param label - edge label
311 * @return true, if any such rules exist
313 public boolean hasEdgeRule(String nodeA, String nodeB, String label) {
314 return this.hasEdgeRule(null, nodeA, nodeB, label);
318 * Checks if any edge rules exist between the two given nodes, in either A|B or B|A order.
320 * @param aVertex - node at one end of the edge
321 * @param bVertex - node at the other end
322 * @return true, if any such rules exist
324 public boolean hasEdgeRule(Vertex aVertex, Vertex bVertex) {
325 return this.hasEdgeRule(aVertex, bVertex, null);
330 * Checks if any edge rules exist between the two given nodes with label, in either A|B or B|A order with edge label.
332 * @param aVertex - node at one end of the edge
333 * @param bVertex - node at the other end
334 * @param label - edge label
335 * @return true, if any such rules exist
337 public boolean hasEdgeRule(Vertex aVertex, Vertex bVertex, String label) {
338 String outType = aVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
339 String inType = bVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
342 return this.hasEdgeRule(outType, inType);
344 return this.hasEdgeRule(outType, inType, label);
349 * Checks if any edge rules exist between the two given node types, in either A|B or B|A order with edge label and edge type.
351 * @param type - type of edge EdgeType.COUSIN | EdgeType.TREE
352 * @param nodeA - node at one end of the edge
353 * @param nodeB - node at the other end
354 * @param label - edge label
355 * @return true, if any such rules exist
357 public boolean hasEdgeRule(EdgeType type, String nodeA, String nodeB, String label) {
358 Filter aToB = filter(
359 where("from").is(nodeA)
362 Filter bToA = filter(
363 where("from").is(nodeB)
367 if (EdgeType.TREE.equals(type)) {
368 aToB = aToB.and(where(EdgeProperty.CONTAINS.toString()).ne(AAIDirection.NONE.toString()));
369 bToA = bToA.and(where(EdgeProperty.CONTAINS.toString()).ne(AAIDirection.NONE.toString()));
370 } else if (EdgeType.COUSIN.equals(type)) {
371 aToB = aToB.and(where(EdgeProperty.CONTAINS.toString()).is(AAIDirection.NONE.toString()));
372 bToA = bToA.and(where(EdgeProperty.CONTAINS.toString()).is(AAIDirection.NONE.toString()));
376 aToB = aToB.and(where(LABEL).is(label));
377 bToA = bToA.and(where(LABEL).is(label));
380 List<Object> results = rulesDoc.read("$.rules.[?]", aToB);
381 results.addAll(rulesDoc.read("$.rules.[?]", bToA));
383 return !results.isEmpty();
387 * Gets all the edge rules that exist between the given node types.
388 * The rules will be phrased in terms of out|in, though this will
389 * also find rules defined as in|out (it will flip the direction in
390 * the EdgeRule object returned accordingly to match out|in).
394 * @return Map<String edgeLabel, EdgeRule rule> where edgeLabel is the label name
395 * @throws AAIException
397 public Map<String, EdgeRule> getEdgeRules(String outType, String inType) {
398 return this.getEdgeRules(outType, inType, null);
402 * Gets all the edge rules that exist between the given node types with given label.
403 * The rules will be phrased in terms of out|in, though this will
404 * also find rules defined as in|out (it will flip the direction in
405 * the EdgeRule object returned accordingly to match out|in).
410 * @return Map<String edgeLabel, EdgeRule rule> where edgeLabel is the label name
411 * @throws AAIException
413 public Map<String, EdgeRule> getEdgeRules(String outType, String inType, String label) {
414 final Map<String, EdgeRule> result = new HashMap<>();
416 for (EdgeType type : EdgeType.values()) {
417 result.putAll(this.getEdgeRules(type, outType, inType, label));
424 * Looks up edge rules for the given node types and the labels specified
430 * @throws NoEdgeRuleFoundException
431 * @throws MultipleEdgeRuleFoundException
433 public Map<String, EdgeRule> getEdgeRulesWithLabels(EdgeType type, String outType, String inType, List<String> labels) throws NoEdgeRuleFoundException, MultipleEdgeRuleFoundException {
434 final Map<String, EdgeRule> result = new HashMap<>();
436 if (labels == null || labels.isEmpty()) {
437 throw new NoEdgeRuleFoundException("No labels specified");
439 for (String label : labels) {
440 EdgeRule er = this.getEdgeRule(type, outType, inType, label);
441 result.put(er.getLabel(), er);
448 * Gets all the edge rules of that edge type that exist between the given node types with given label.
449 * The rules will be phrased in terms of out|in, though this will
450 * also find rules defined as in|out (it will flip the direction in
451 * the EdgeRule object returned accordingly to match out|in).
458 * @throws AAIException
460 public Map<String, EdgeRule> getEdgeRules(EdgeType type, String outType, String inType, String label) {
461 final Map<String, EdgeRule> result = new HashMap<>();
463 this.getEdgeRulesFromJson(type, outType, inType, label).forEach(edgeRuleJson -> {
464 EdgeRule edgeRule = this.buildRule(edgeRuleJson);
465 result.put(edgeRule.getLabel(), edgeRule);
467 this.getEdgeRulesFromJson(type, inType, outType, label).forEach(erj -> {
468 EdgeRule edgeRule = this.flipDirection(this.buildRule(erj));
469 if (!result.containsKey(edgeRule.getLabel())) {
470 result.put(edgeRule.getLabel(), edgeRule);
479 * Gets all the edge rules of that edge type that exist between the given node types.
480 * The rules will be phrased in terms of out|in, though this will
481 * also find rules defined as in|out (it will flip the direction in
482 * the EdgeRule object returned accordingly to match out|in).
488 * @throws AAIException
490 public Map<String, EdgeRule> getEdgeRules(EdgeType type, String outType, String inType) {
491 return this.getEdgeRules(type, outType, inType, null);
495 * Gets the edge rule of the given type that exists between A and B.
496 * Will check B|A as well, and flips the direction accordingly if that succeeds
497 * to match the expected A|B return.
499 * @param type - the type of edge you're looking for
500 * @param nodeA - first node type
501 * @param nodeB - second node type
502 * @return EdgeRule describing the rule in terms of A|B, if there is any such rule
503 * @throws AAIException if no such edge exists
505 public EdgeRule getEdgeRule(EdgeType type, String nodeA, String nodeB) throws AAIException {
506 return this.getEdgeRule(type, nodeA, nodeB, null);
510 * Gets the edge rule of the given type that exists between A and B with edge label.
511 * Will check B|A as well, and flips the direction accordingly if that succeeds
512 * to match the expected A|B return.
514 * @param type - the type of edge you're looking for
515 * @param nodeA - first node type
516 * @param nodeB - second node type
517 * @param label - edge label
518 * @return EdgeRule describing the rule in terms of A|B, if there is any such rule
519 * @throws MultipleEdgeRuleFoundException
520 * @throws AAIException if no such edge exists
522 public EdgeRule getEdgeRule(EdgeType type, String nodeA, String nodeB, String label) throws NoEdgeRuleFoundException, MultipleEdgeRuleFoundException {
524 final StringBuilder errorMsg = new StringBuilder();
525 errorMsg.append(type.toString())
526 .append(" edge rule between ")
527 .append(nodeA).append(" and ").append(nodeB);
529 errorMsg.append(" with label ").append(label);
533 Map<String, EdgeRule> edgeRules = this.getEdgeRules(type, nodeA, nodeB, label);
536 if (edgeRules.isEmpty()) {
538 throw new NoEdgeRuleFoundException("no " + errorMsg);
540 } else if (edgeRules.size() == 1) {
542 edgeRule = edgeRules.values().iterator().next();
546 Optional<EdgeRule> optionalEdgeRule = Optional.empty();
549 optionalEdgeRule = this.getDefaultEdgeRule(edgeRules);
550 } catch (MultipleEdgeRuleFoundException e) {
551 throw new MultipleEdgeRuleFoundException("multiple default edge rule exists " + errorMsg);
554 edgeRule = optionalEdgeRule.orElseThrow(() -> new MultipleEdgeRuleFoundException("multiple edge rule exists with no default " + errorMsg));
561 private Optional<EdgeRule> getDefaultEdgeRule(Map<String, EdgeRule> edgeRules) throws MultipleEdgeRuleFoundException {
563 EdgeRule edgeRule = null;
566 for (Map.Entry<String, EdgeRule> entry : edgeRules.entrySet()) {
567 if (entry.getValue().isDefault()) {
568 edgeRule = entry.getValue();
573 if (numDefaults > 1) {
574 throw new MultipleEdgeRuleFoundException("");
577 if (edgeRule == null) {
578 return Optional.empty();
580 return Optional.of(edgeRule);
585 * Gets the rules from the edge rules Json
588 * @param nodeA - start node
589 * @param nodeB - end node
590 * @param label - edge label to filter on
593 private List<Map<String, String>> getEdgeRulesFromJson(EdgeType type, String nodeA, String nodeB, String label) {
595 return rulesDoc.read("$.rules.[?]", buildFilter(type, nodeA, nodeB));
597 return rulesDoc.read("$.rules.[?]", buildFilter(type, nodeA, nodeB, label));
602 * Builds a JsonPath filter to search for an edge from nodeA to nodeB with the given edge type (cousin or parent/child)
605 * @param nodeA - start node
606 * @param nodeB - end node
609 private Filter buildFilter(EdgeType type, String nodeA, String nodeB) {
610 return this.buildFilter(type, nodeA, nodeB, null);
613 private Filter buildFilter(EdgeType type, String nodeA, String nodeB, String label) {
614 if (EdgeType.COUSIN.equals(type)) {
616 where("from").is(nodeA)
618 .and(EdgeProperty.CONTAINS.toString()).is(AAIDirection.NONE.toString())
621 f = f.and(where(LABEL).is(label));
627 where("from").is(nodeA).and("to").is(nodeB).and(EdgeProperty.CONTAINS.toString()).is(DIRECTION_NOTATION)).or(
628 where("from").is(nodeA).and("to").is(nodeB).and(EdgeProperty.CONTAINS.toString()).is(NOT_DIRECTION_NOTATION)
634 * Puts the give edge rule information into an EdgeRule object.
636 * @param map edge rule property map
637 * @return EdgeRule containing that information
639 private EdgeRule buildRule(Map<String, String> map) {
640 Map<String, String> edge = new EdgePropertyMap<>();
643 EdgeRule rule = new EdgeRule();
644 rule.setLabel(edge.get(LABEL));
645 rule.setDirection(edge.get("direction"));
646 rule.setMultiplicityRule(edge.get("multiplicity"));
647 rule.setContains(edge.get(EdgeProperty.CONTAINS.toString()));
648 rule.setDeleteOtherV(edge.get(EdgeProperty.DELETE_OTHER_V.toString()));
649 rule.setServiceInfrastructure(edge.get(EdgeProperty.SVC_INFRA.toString()));
650 rule.setPreventDelete(edge.get(EdgeProperty.PREVENT_DELETE.toString()));
651 rule.setTo(edge.get("to"));
652 rule.setFrom(edge.get("from"));
653 if (edge.containsKey("default")) {
654 rule.setIsDefault(edge.get("default"));
657 if(rule.getFrom().equals(rule.getTo())){
658 return this.flipDirection(rule);
665 * If getEdgeRule gets a request for A|B, and it finds something as B|A, the caller still expects
666 * the returned EdgeRule to reflect A|B directionality. This helper method flips B|A direction to
667 * match this expectation.
669 * @param rule whose direction needs flipped
670 * @return the updated rule
672 private EdgeRule flipDirection(EdgeRule rule) {
673 if (Direction.IN.equals(rule.getDirection())) {
674 rule.setDirection(Direction.OUT);
676 } else if (Direction.OUT.equals(rule.getDirection())) {
677 rule.setDirection(Direction.IN);
679 } else { //direction is BOTH, flipping both is still both
685 * Gets the edge rule of the given type that exists between A and B.
686 * Will check B|A as well, and flips the direction accordingly if that succeeds
687 * to match the expected A|B return.
689 * @param type - the type of edge you're looking for
690 * @param aVertex - first node type
691 * @param bVertex - second node type
692 * @return EdgeRule describing the rule in terms of A|B, if there is any such rule
693 * @throws AAIException if no such edge exists
695 public EdgeRule getEdgeRule(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
696 return this.getEdgeRule(type, aVertex, bVertex, null);
700 * Gets the edge rule of the given type that exists between A and B with label.
701 * Will check B|A as well, and flips the direction accordingly if that succeeds
702 * to match the expected A|B return.
704 * @param type - the type of edge you're looking for
705 * @param aVertex - first node type
706 * @param bVertex - second node type
707 * @param label - edge label
708 * @return EdgeRule describing the rule in terms of A|B, if there is any such rule
709 * @throws AAIException if no such edge exists
711 public EdgeRule getEdgeRule(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
712 String outType = aVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
713 String inType = bVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
715 return this.getEdgeRule(type, outType, inType, label);
721 * Validate multiplicity.
723 * @param rule the rule
724 * @param aVertex the out vertex
725 * @param bVertex the in vertex
726 * @return true, if successful
727 * @throws AAIException the AAI exception
729 private Optional<String> validateMultiplicity(EdgeRule rule, GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) {
734 if (rule.getDirection().equals(Direction.OUT)) {
737 } else if (rule.getDirection().equals(Direction.IN)) {
742 String aVertexType = a.<String>property(AAIProperties.NODE_TYPE).orElse(null);
743 String bVertexType = b.<String>property(AAIProperties.NODE_TYPE).orElse(null);
744 String label = rule.getLabel();
745 MultiplicityRule multiplicityRule = rule.getMultiplicityRule();
746 List<Edge> outEdges = traversalSource.V(a).outE(label).where(__.inV().has(AAIProperties.NODE_TYPE, bVertexType)).toList();
747 List<Edge> inEdges = traversalSource.V(b).inE(label).where(__.outV().has(AAIProperties.NODE_TYPE, aVertexType)).toList();
749 final String msg = "multiplicity rule violated: only one edge can exist with label: ";
750 if (multiplicityRule.equals(MultiplicityRule.ONE2ONE)) {
751 if (!inEdges.isEmpty() || !outEdges.isEmpty() ) {
752 detail = msg + label + " between " + aVertexType + " and " + bVertexType;
754 } else if (multiplicityRule.equals(MultiplicityRule.ONE2MANY)) {
755 if (!inEdges.isEmpty()) {
756 detail = msg + label + " between " + aVertexType + " and " + bVertexType;
758 } else if (multiplicityRule.equals(MultiplicityRule.MANY2ONE)) {
759 if (!outEdges.isEmpty()) {
760 detail = msg + label + " between " + aVertexType + " and " + bVertexType;
764 if (!"".equals(detail)) {
765 return Optional.of(detail);
767 return Optional.empty();
774 * Verifies that all required properties are defined in the given edge rule.
775 * If they are not, throws a RuntimeException.
777 * @param rule - Map<String edge property, String edge property value> representing
780 private void verifyRule(Map<String, String> rule) {
781 for (EdgeProperty prop : EdgeProperty.values()) {
783 // Description is not required as it is only set for v12 versions
784 if("description".equals(prop.toString())){
787 if (!rule.containsKey(prop.toString())) {
788 /* Throws RuntimeException as rule definition errors
789 * cannot be recovered from, and should never happen anyway
790 * because these are configuration files, so requiring all
791 * downstream code to check for this exception seems inappropriate.
792 * It's instantiated with an AAIException to make sure all
793 * relevant information is present in the error message.
795 throw new RuntimeException(new AAIException("AAI_4005",
796 "Rule between " + rule.get("from") + " and " + rule.get("to") +
797 " is missing property " + prop + "."));
803 * Reads all the edge rules from the loaded json file.
805 * @return List<Map<String edge property, String edge property value>>
806 * Each map represents a rule read from the json.
808 private List<Map<String, String>> readRules() {
809 return readRules(null);
813 * Reads the edge rules from the loaded json file, using the given filter
814 * to get specific rules. If filter is null, will get all rules.
816 * @param filter - may be null to indicate get all
817 * @return List<Map<String edge property, String edge property value>>
818 * Each map represents a rule read from the json.
820 private List<Map<String, String>> readRules(Filter filter) {
821 List<Map<String, String>> results;
822 if (filter == null) { //no filter means get all
823 results = rulesDoc.read("$.rules.*");
825 results = rulesDoc.read("$.rules.[?]", filter);
827 for (Map<String, String> result : results) {
834 * Gets all the edge rules we define.
836 * @return Multimap<String "from|to", EdgeRule rule>
838 public Multimap<String, EdgeRule> getAllRules() {
839 Multimap<String, EdgeRule> result = ArrayListMultimap.create();
841 List<Map<String, String>> rules = readRules();
842 for (Map<String, String> rule : rules) {
843 EdgeRule er = buildRule(rule);
844 String name = rule.get("from") + "|" + rule.get("to");
845 result.put(name, er);
852 * Gets all edge rules that define a child relationship from
853 * the given node type.
858 public Set<EdgeRule> getChildren(String nodeType) {
860 final Filter filter = filter(
861 where("from").is(nodeType).and(EdgeProperty.CONTAINS.toString()).is(DIRECTION_NOTATION)
862 ).or(where("to").is(nodeType).and(EdgeProperty.CONTAINS.toString()).is(NOT_DIRECTION_NOTATION));
864 final List<Map<String, String>> rules = readRules(filter);
865 final Set<EdgeRule> result = new HashSet<>();
866 rules.forEach(item -> {
868 result.add(buildRule(item));
875 private static class Helper {
876 private static final EdgeRules INSTANCE = new EdgeRules();
877 private static final Map<Version, EdgeRules> INSTANCEMAP = new ConcurrentHashMap<>();
881 private static EdgeRules getEdgeRulesByFilename(String rulesFilename) {
882 return new EdgeRules(rulesFilename);
885 private static EdgeRules getVersionedEdgeRules(Version v) {
886 if (Version.isLatest(v)) {
889 if (!INSTANCEMAP.containsKey(v)) {
890 INSTANCEMAP.put(v, new EdgeRules(v));
892 return INSTANCEMAP.get(v);