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;
28 import java.util.HashMap;
29 import java.util.HashSet;
30 import java.util.List;
32 import java.util.Map.Entry;
33 import java.util.Optional;
34 import java.util.Scanner;
36 import java.util.concurrent.ConcurrentHashMap;
38 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
39 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
40 import org.apache.tinkerpop.gremlin.structure.Direction;
41 import org.apache.tinkerpop.gremlin.structure.Edge;
42 import org.apache.tinkerpop.gremlin.structure.Vertex;
43 import org.onap.aai.db.props.AAIProperties;
44 import org.onap.aai.exceptions.AAIException;
45 import org.onap.aai.introspection.Version;
46 import org.onap.aai.serialization.db.exceptions.EdgeMultiplicityException;
47 import org.onap.aai.serialization.db.exceptions.MultipleEdgeRuleFoundException;
48 import org.onap.aai.serialization.db.exceptions.NoEdgeRuleFoundException;
50 import com.att.eelf.configuration.EELFLogger;
51 import com.att.eelf.configuration.EELFManager;
52 import com.google.common.collect.ArrayListMultimap;
53 import com.google.common.collect.Multimap;
54 import com.jayway.jsonpath.DocumentContext;
55 import com.jayway.jsonpath.Filter;
56 import com.jayway.jsonpath.JsonPath;
58 public class EdgeRules {
60 private static final String LABEL = "label";
62 private static final String NOT_DIRECTION_NOTATION = "!${direction}";
64 private static final String DIRECTION_NOTATION = "${direction}";
66 private EELFLogger logger = EELFManager.getInstance().getLogger(EdgeRules.class);
68 private DocumentContext rulesDoc;
71 * Loads the most recent DbEdgeRules json file for later parsing.
72 * Only need most recent version for actual A&AI operations that call this class;
73 * the old ones are only used in tests.
77 String json = this.getEdgeRuleJson(Version.getLatest());
78 rulesDoc = JsonPath.parse(json);
82 private EdgeRules(String rulesFilename) {
83 String json = this.getEdgeRuleJson(rulesFilename);
84 rulesDoc = JsonPath.parse(json);
88 * Loads the versioned DbEdgeRules json file for later parsing.
90 private EdgeRules(Version version) {
91 String json = this.getEdgeRuleJson(version);
92 rulesDoc = JsonPath.parse(json);
96 * Gets the single instance of EdgeRules.
98 * @return single instance of EdgeRules
100 public static EdgeRules getInstance() {
101 return Helper.INSTANCE;
106 * Gets the versioned instance of EdgeRules.
108 * @return versioned instance of EdgeRules
110 public static EdgeRules getInstance(Version v) {
111 return Helper.getVersionedEdgeRules(v);
116 * Loads edge rules from the given file.
118 * @param rulesFilename - name of the file to load rules from
119 * @return the EdgeRules instance
121 public static EdgeRules getInstance(String rulesFilename) {
122 return Helper.getEdgeRulesByFilename(rulesFilename);
125 private String getEdgeRuleJson(String rulesFilename) {
126 InputStream is = getClass().getResourceAsStream(rulesFilename);
128 Scanner scanner = new Scanner(is);
129 String json = scanner.useDelimiter("\\Z").next();
135 private String getEdgeRuleJson(Version version) {
136 return this.getEdgeRuleJson("/dbedgerules/DbEdgeRules_" + version.toString() + ".json");
140 * Adds the tree edge.
142 * @param aVertex the out vertex
143 * @param bVertex the in vertex
145 * @throws AAIException the AAI exception
147 public Edge addTreeEdge(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException {
148 return this.addEdge(EdgeType.TREE, traversalSource, aVertex, bVertex, false, null);
154 * @param aVertex the out vertex
155 * @param bVertex the in vertex
157 * @throws AAIException the AAI exception
159 public Edge addEdge(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException {
160 return this.addEdge(traversalSource, aVertex, bVertex, null);
163 public Edge addEdge(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
164 return this.addEdge(EdgeType.COUSIN, traversalSource, aVertex, bVertex, false, label);
168 * Adds the tree edge.
170 * @param aVertex the out vertex
171 * @param bVertex the in vertex
173 * @throws AAIException the AAI exception
175 public Edge addTreeEdgeIfPossible(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException {
176 return this.addEdge(EdgeType.TREE, traversalSource, aVertex, bVertex, true, null);
182 * @param aVertex the out vertex
183 * @param bVertex the in vertex
185 * @throws AAIException the AAI exception
187 public Edge addEdgeIfPossible(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) throws AAIException {
188 return this.addEdgeIfPossible(traversalSource, aVertex, bVertex, null);
191 public Edge addEdgeIfPossible(GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
192 return this.addEdge(EdgeType.COUSIN, traversalSource, aVertex, bVertex, true, label);
198 * @param type the type
199 * @param aVertex the out vertex
200 * @param bVertex the in vertex
202 * @throws AAIException the AAI exception
204 private Edge addEdge(EdgeType type, GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex, boolean isBestEffort, String label) throws AAIException {
206 EdgeRule rule = this.getEdgeRule(type, aVertex, bVertex, label);
210 Optional<String> message = this.validateMultiplicity(rule, traversalSource, aVertex, bVertex);
212 if (message.isPresent() && !isBestEffort) {
213 throw new EdgeMultiplicityException(message.get());
215 if (!message.isPresent()) {
216 if (rule.getDirection().equals(Direction.OUT)) {
217 e = aVertex.addEdge(rule.getLabel(), bVertex);
218 } else if (rule.getDirection().equals(Direction.IN)) {
219 e = bVertex.addEdge(rule.getLabel(), aVertex);
222 this.addProperties(e, rule);
228 * Adds the properties.
230 * @param edge the edge
231 * @param rule the rule
233 public void addProperties(Edge edge, EdgeRule rule) {
235 // In DbEdgeRules.EdgeRules -- What we have as "edgeRule" is a comma-delimited set of strings.
236 // The first item is the edgeLabel.
237 // The second in the list is always "direction" which is always OUT for the way we've implemented it.
238 // Items starting at "firstTagIndex" and up are all assumed to be booleans that map according to
239 // tags as defined in EdgeInfoMap.
240 // Note - if they are tagged as 'reverse', that means they get the tag name with "-REV" on it
241 Map<EdgeProperty, String> propMap = rule.getEdgeProperties();
243 for (Entry<EdgeProperty, String> entry : propMap.entrySet()) {
244 edge.property(entry.getKey().toString(), entry.getValue());
249 * Checks if any edge rules exist between the two given node types, in either A|B or B|A order.
251 * @param nodeA - node at one end of the edge
252 * @param nodeB - node at the other end
253 * @return true, if any such rules exist
255 public boolean hasEdgeRule(String nodeA, String nodeB) {
256 return this.hasEdgeRule(nodeA, nodeB, null);
260 * 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.
262 * @param nodeA - node at one end of the edge
263 * @param nodeB - node at the other end
264 * @return true, if any such rules exist
266 public boolean hasTreeEdgeRule(String nodeA, String nodeB) {
267 return this.hasEdgeRule(EdgeType.TREE, nodeA, nodeB, null);
271 * 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.
273 * @param nodeA - node at one end of the edge
274 * @param nodeB - node at the other end
275 * @param label - edge label
276 * @return true, if any such rules exist
278 public boolean hasCousinEdgeRule(String nodeA, String nodeB, String label) {
279 return this.hasEdgeRule(EdgeType.COUSIN, nodeA, nodeB, label);
283 * Checks if any edge rules exist between the two given nodes with contains-other-v !NONE, in either A|B or B|A order.
285 * @param aVertex - node at one end of the edge
286 * @param bVertex - node at the other end
287 * @return true, if any such rules exist
289 public boolean hasTreeEdgeRule(Vertex aVertex, Vertex bVertex) {
290 String outType = aVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
291 String inType = bVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
292 return this.hasTreeEdgeRule(outType, inType);
296 * 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.
298 * @param aVertex - node at one end of the edge
299 * @param bVertex - node at the other end
300 * @param label - edge label
301 * @return true, if any such rules exist
303 public boolean hasCousinEdgeRule(Vertex aVertex, Vertex bVertex, String label) {
304 String outType = aVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
305 String inType = bVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
306 return this.hasCousinEdgeRule(outType, inType, label);
310 * Checks if any edge rules exist between the two given nodes w/ edge label, in either A|B or B|A order.
312 * @param nodeA - node at one end of the edge
313 * @param nodeB - node at the other end
314 * @param label - edge label
315 * @return true, if any such rules exist
317 public boolean hasEdgeRule(String nodeA, String nodeB, String label) {
318 return this.hasEdgeRule(null, nodeA, nodeB, label);
322 * Checks if any edge rules exist between the two given nodes, in either A|B or B|A order.
324 * @param aVertex - node at one end of the edge
325 * @param bVertex - node at the other end
326 * @return true, if any such rules exist
328 public boolean hasEdgeRule(Vertex aVertex, Vertex bVertex) {
329 return this.hasEdgeRule(aVertex, bVertex, null);
334 * Checks if any edge rules exist between the two given nodes with label, in either A|B or B|A order with edge label.
336 * @param aVertex - node at one end of the edge
337 * @param bVertex - node at the other end
338 * @param label - edge label
339 * @return true, if any such rules exist
341 public boolean hasEdgeRule(Vertex aVertex, Vertex bVertex, String label) {
342 String outType = aVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
343 String inType = bVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
346 return this.hasEdgeRule(outType, inType);
348 return this.hasEdgeRule(outType, inType, label);
353 * 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.
355 * @param type - type of edge EdgeType.COUSIN | EdgeType.TREE
356 * @param nodeA - node at one end of the edge
357 * @param nodeB - node at the other end
358 * @param label - edge label
359 * @return true, if any such rules exist
361 public boolean hasEdgeRule(EdgeType type, String nodeA, String nodeB, String label) {
362 Filter aToB = filter(
363 where("from").is(nodeA)
366 Filter bToA = filter(
367 where("from").is(nodeB)
371 if (EdgeType.TREE.equals(type)) {
372 aToB = aToB.and(where(EdgeProperty.CONTAINS.toString()).ne(AAIDirection.NONE.toString()));
373 bToA = bToA.and(where(EdgeProperty.CONTAINS.toString()).ne(AAIDirection.NONE.toString()));
374 } else if (EdgeType.COUSIN.equals(type)) {
375 aToB = aToB.and(where(EdgeProperty.CONTAINS.toString()).is(AAIDirection.NONE.toString()));
376 bToA = bToA.and(where(EdgeProperty.CONTAINS.toString()).is(AAIDirection.NONE.toString()));
380 aToB = aToB.and(where(LABEL).is(label));
381 bToA = bToA.and(where(LABEL).is(label));
384 List<Object> results = rulesDoc.read("$.rules.[?]", aToB);
385 results.addAll(rulesDoc.read("$.rules.[?]", bToA));
387 return !results.isEmpty();
391 * Gets all the edge rules that exist between the given node types.
392 * The rules will be phrased in terms of out|in, though this will
393 * also find rules defined as in|out (it will flip the direction in
394 * the EdgeRule object returned accordingly to match out|in).
398 * @return Map<String edgeLabel, EdgeRule rule> where edgeLabel is the label name
399 * @throws AAIException
401 public Map<String, EdgeRule> getEdgeRules(String outType, String inType) {
402 return this.getEdgeRules(outType, inType, null);
406 * Gets all the edge rules that exist between the given node types with given label.
407 * The rules will be phrased in terms of out|in, though this will
408 * also find rules defined as in|out (it will flip the direction in
409 * the EdgeRule object returned accordingly to match out|in).
414 * @return Map<String edgeLabel, EdgeRule rule> where edgeLabel is the label name
415 * @throws AAIException
417 public Map<String, EdgeRule> getEdgeRules(String outType, String inType, String label) {
418 final Map<String, EdgeRule> result = new HashMap<>();
420 for (EdgeType type : EdgeType.values()) {
421 result.putAll(this.getEdgeRules(type, outType, inType, label));
428 * Looks up edge rules for the given node types and the labels specified
434 * @throws NoEdgeRuleFoundException
435 * @throws MultipleEdgeRuleFoundException
437 public Map<String, EdgeRule> getEdgeRulesWithLabels(EdgeType type, String outType, String inType, List<String> labels) throws NoEdgeRuleFoundException, MultipleEdgeRuleFoundException {
438 final Map<String, EdgeRule> result = new HashMap<>();
440 if (labels == null || labels.isEmpty()) {
441 throw new NoEdgeRuleFoundException("No labels specified");
443 for (String label : labels) {
444 EdgeRule er = this.getEdgeRule(type, outType, inType, label);
445 result.put(er.getLabel(), er);
452 * Gets all the edge rules of that edge type that exist between the given node types with given label.
453 * The rules will be phrased in terms of out|in, though this will
454 * also find rules defined as in|out (it will flip the direction in
455 * the EdgeRule object returned accordingly to match out|in).
462 * @throws AAIException
464 public Map<String, EdgeRule> getEdgeRules(EdgeType type, String outType, String inType, String label) {
465 final Map<String, EdgeRule> result = new HashMap<>();
467 this.getEdgeRulesFromJson(type, outType, inType, label).forEach(edgeRuleJson -> {
468 EdgeRule edgeRule = this.buildRule(edgeRuleJson);
469 result.put(edgeRule.getLabel(), edgeRule);
471 this.getEdgeRulesFromJson(type, inType, outType, label).forEach(erj -> {
472 EdgeRule edgeRule = this.flipDirection(this.buildRule(erj));
473 if (!result.containsKey(edgeRule.getLabel())) {
474 result.put(edgeRule.getLabel(), edgeRule);
483 * Gets all the edge rules of that edge type that exist between the given node types.
484 * The rules will be phrased in terms of out|in, though this will
485 * also find rules defined as in|out (it will flip the direction in
486 * the EdgeRule object returned accordingly to match out|in).
492 * @throws AAIException
494 public Map<String, EdgeRule> getEdgeRules(EdgeType type, String outType, String inType) {
495 return this.getEdgeRules(type, outType, inType, null);
499 * Gets the edge rule of the given type that exists between A and B.
500 * Will check B|A as well, and flips the direction accordingly if that succeeds
501 * to match the expected A|B return.
503 * @param type - the type of edge you're looking for
504 * @param nodeA - first node type
505 * @param nodeB - second node type
506 * @return EdgeRule describing the rule in terms of A|B, if there is any such rule
507 * @throws AAIException if no such edge exists
509 public EdgeRule getEdgeRule(EdgeType type, String nodeA, String nodeB) throws AAIException {
510 return this.getEdgeRule(type, nodeA, nodeB, null);
514 * Gets the edge rule of the given type that exists between A and B with edge label.
515 * Will check B|A as well, and flips the direction accordingly if that succeeds
516 * to match the expected A|B return.
518 * @param type - the type of edge you're looking for
519 * @param nodeA - first node type
520 * @param nodeB - second node type
521 * @param label - edge label
522 * @return EdgeRule describing the rule in terms of A|B, if there is any such rule
523 * @throws MultipleEdgeRuleFoundException
524 * @throws AAIException if no such edge exists
526 public EdgeRule getEdgeRule(EdgeType type, String nodeA, String nodeB, String label) throws NoEdgeRuleFoundException, MultipleEdgeRuleFoundException {
528 final StringBuilder errorMsg = new StringBuilder();
529 errorMsg.append(type.toString())
530 .append(" edge rule between ")
531 .append(nodeA).append(" and ").append(nodeB);
533 errorMsg.append(" with label ").append(label);
537 Map<String, EdgeRule> edgeRules = this.getEdgeRules(type, nodeA, nodeB, label);
540 if (edgeRules.isEmpty()) {
542 throw new NoEdgeRuleFoundException("no " + errorMsg);
544 } else if (edgeRules.size() == 1) {
546 edgeRule = edgeRules.values().iterator().next();
550 Optional<EdgeRule> optionalEdgeRule = Optional.empty();
553 optionalEdgeRule = this.getDefaultEdgeRule(edgeRules);
554 } catch (MultipleEdgeRuleFoundException e) {
555 throw new MultipleEdgeRuleFoundException("multiple default edge rule exists " + errorMsg);
558 edgeRule = optionalEdgeRule.orElseThrow(() -> new MultipleEdgeRuleFoundException("multiple edge rule exists with no default " + errorMsg));
565 private Optional<EdgeRule> getDefaultEdgeRule(Map<String, EdgeRule> edgeRules) throws MultipleEdgeRuleFoundException {
567 EdgeRule edgeRule = null;
570 for (Map.Entry<String, EdgeRule> entry : edgeRules.entrySet()) {
571 if (entry.getValue().isDefault()) {
572 edgeRule = entry.getValue();
577 if (numDefaults > 1) {
578 throw new MultipleEdgeRuleFoundException("");
581 if (edgeRule == null) {
582 return Optional.empty();
584 return Optional.of(edgeRule);
589 * Gets the rules from the edge rules Json
592 * @param nodeA - start node
593 * @param nodeB - end node
594 * @param label - edge label to filter on
597 private List<Map<String, String>> getEdgeRulesFromJson(EdgeType type, String nodeA, String nodeB, String label) {
599 return rulesDoc.read("$.rules.[?]", buildFilter(type, nodeA, nodeB));
601 return rulesDoc.read("$.rules.[?]", buildFilter(type, nodeA, nodeB, label));
606 * Builds a JsonPath filter to search for an edge from nodeA to nodeB with the given edge type (cousin or parent/child)
609 * @param nodeA - start node
610 * @param nodeB - end node
613 private Filter buildFilter(EdgeType type, String nodeA, String nodeB) {
614 return this.buildFilter(type, nodeA, nodeB, null);
617 private Filter buildFilter(EdgeType type, String nodeA, String nodeB, String label) {
618 if (EdgeType.COUSIN.equals(type)) {
620 where("from").is(nodeA)
622 .and(EdgeProperty.CONTAINS.toString()).is(AAIDirection.NONE.toString())
625 f = f.and(where(LABEL).is(label));
631 where("from").is(nodeA).and("to").is(nodeB).and(EdgeProperty.CONTAINS.toString()).is(DIRECTION_NOTATION)).or(
632 where("from").is(nodeA).and("to").is(nodeB).and(EdgeProperty.CONTAINS.toString()).is(NOT_DIRECTION_NOTATION)
638 * Puts the give edge rule information into an EdgeRule object.
640 * @param map edge rule property map
641 * @return EdgeRule containing that information
643 private EdgeRule buildRule(Map<String, String> map) {
644 Map<String, String> edge = new EdgePropertyMap<>();
647 EdgeRule rule = new EdgeRule();
648 rule.setLabel(edge.get(LABEL));
649 rule.setDirection(edge.get("direction"));
650 rule.setMultiplicityRule(edge.get("multiplicity"));
651 rule.setContains(edge.get(EdgeProperty.CONTAINS.toString()));
652 rule.setDeleteOtherV(edge.get(EdgeProperty.DELETE_OTHER_V.toString()));
653 rule.setServiceInfrastructure(edge.get(EdgeProperty.SVC_INFRA.toString()));
654 rule.setPreventDelete(edge.get(EdgeProperty.PREVENT_DELETE.toString()));
655 if (edge.containsKey("default")) {
656 rule.setIsDefault(edge.get("default"));
663 * If getEdgeRule gets a request for A|B, and it finds something as B|A, the caller still expects
664 * the returned EdgeRule to reflect A|B directionality. This helper method flips B|A direction to
665 * match this expectation.
667 * @param rule whose direction needs flipped
668 * @return the updated rule
670 private EdgeRule flipDirection(EdgeRule rule) {
671 if (Direction.IN.equals(rule.getDirection())) {
672 rule.setDirection(Direction.OUT);
674 } else if (Direction.OUT.equals(rule.getDirection())) {
675 rule.setDirection(Direction.IN);
677 } else { //direction is BOTH, flipping both is still both
683 * Gets the edge rule of the given type that exists between A and B.
684 * Will check B|A as well, and flips the direction accordingly if that succeeds
685 * to match the expected A|B return.
687 * @param type - the type of edge you're looking for
688 * @param aVertex - first node type
689 * @param bVertex - second node type
690 * @return EdgeRule describing the rule in terms of A|B, if there is any such rule
691 * @throws AAIException if no such edge exists
693 public EdgeRule getEdgeRule(EdgeType type, Vertex aVertex, Vertex bVertex) throws AAIException {
694 return this.getEdgeRule(type, aVertex, bVertex, null);
698 * Gets the edge rule of the given type that exists between A and B with label.
699 * Will check B|A as well, and flips the direction accordingly if that succeeds
700 * to match the expected A|B return.
702 * @param type - the type of edge you're looking for
703 * @param aVertex - first node type
704 * @param bVertex - second node type
705 * @param label - edge label
706 * @return EdgeRule describing the rule in terms of A|B, if there is any such rule
707 * @throws AAIException if no such edge exists
709 public EdgeRule getEdgeRule(EdgeType type, Vertex aVertex, Vertex bVertex, String label) throws AAIException {
710 String outType = aVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
711 String inType = bVertex.<String>property(AAIProperties.NODE_TYPE).orElse(null);
713 return this.getEdgeRule(type, outType, inType, label);
719 * Validate multiplicity.
721 * @param rule the rule
722 * @param aVertex the out vertex
723 * @param bVertex the in vertex
724 * @return true, if successful
725 * @throws AAIException the AAI exception
727 private Optional<String> validateMultiplicity(EdgeRule rule, GraphTraversalSource traversalSource, Vertex aVertex, Vertex bVertex) {
732 if (rule.getDirection().equals(Direction.OUT)) {
735 } else if (rule.getDirection().equals(Direction.IN)) {
740 String aVertexType = a.<String>property(AAIProperties.NODE_TYPE).orElse(null);
741 String bVertexType = b.<String>property(AAIProperties.NODE_TYPE).orElse(null);
742 String label = rule.getLabel();
743 MultiplicityRule multiplicityRule = rule.getMultiplicityRule();
744 List<Edge> outEdges = traversalSource.V(a).outE(label).where(__.inV().has(AAIProperties.NODE_TYPE, bVertexType)).toList();
745 List<Edge> inEdges = traversalSource.V(b).inE(label).where(__.outV().has(AAIProperties.NODE_TYPE, aVertexType)).toList();
747 final String msg = "multiplicity rule violated: only one edge can exist with label: ";
748 if (multiplicityRule.equals(MultiplicityRule.ONE2ONE)) {
749 if (!inEdges.isEmpty() || !outEdges.isEmpty() ) {
750 detail = msg + label + " between " + aVertexType + " and " + bVertexType;
752 } else if (multiplicityRule.equals(MultiplicityRule.ONE2MANY)) {
753 if (!inEdges.isEmpty()) {
754 detail = msg + label + " between " + aVertexType + " and " + bVertexType;
756 } else if (multiplicityRule.equals(MultiplicityRule.MANY2ONE)) {
757 if (!outEdges.isEmpty()) {
758 detail = msg + label + " between " + aVertexType + " and " + bVertexType;
762 if (!"".equals(detail)) {
763 return Optional.of(detail);
765 return Optional.empty();
772 * Verifies that all required properties are defined in the given edge rule.
773 * If they are not, throws a RuntimeException.
775 * @param rule - Map<String edge property, String edge property value> representing
778 private void verifyRule(Map<String, String> rule) {
779 for (EdgeProperty prop : EdgeProperty.values()) {
780 if (!rule.containsKey(prop.toString())) {
781 /* Throws RuntimeException as rule definition errors
782 * cannot be recovered from, and should never happen anyway
783 * because these are configuration files, so requiring all
784 * downstream code to check for this exception seems inappropriate.
785 * It's instantiated with an AAIException to make sure all
786 * relevant information is present in the error message.
788 throw new RuntimeException(new AAIException("AAI_4005",
789 "Rule between " + rule.get("from") + " and " + rule.get("to") +
790 " is missing property " + prop + "."));
796 * Reads all the edge rules from the loaded json file.
798 * @return List<Map<String edge property, String edge property value>>
799 * Each map represents a rule read from the json.
801 private List<Map<String, String>> readRules() {
802 return readRules(null);
806 * Reads the edge rules from the loaded json file, using the given filter
807 * to get specific rules. If filter is null, will get all rules.
809 * @param filter - may be null to indicate get all
810 * @return List<Map<String edge property, String edge property value>>
811 * Each map represents a rule read from the json.
813 private List<Map<String, String>> readRules(Filter filter) {
814 List<Map<String, String>> results;
815 if (filter == null) { //no filter means get all
816 results = rulesDoc.read("$.rules.*");
818 results = rulesDoc.read("$.rules.[?]", filter);
820 for (Map<String, String> result : results) {
827 * Gets all the edge rules we define.
829 * @return Multimap<String "from|to", EdgeRule rule>
831 public Multimap<String, EdgeRule> getAllRules() {
832 Multimap<String, EdgeRule> result = ArrayListMultimap.create();
834 List<Map<String, String>> rules = readRules();
835 for (Map<String, String> rule : rules) {
836 EdgeRule er = buildRule(rule);
837 String name = rule.get("from") + "|" + rule.get("to");
838 result.put(name, er);
845 * Gets all edge rules that define a child relationship from
846 * the given node type.
851 public Set<EdgeRule> getChildren(String nodeType) {
853 final Filter filter = filter(
854 where("from").is(nodeType).and(EdgeProperty.CONTAINS.toString()).is(DIRECTION_NOTATION)
855 ).or(where("to").is(nodeType).and(EdgeProperty.CONTAINS.toString()).is(NOT_DIRECTION_NOTATION));
857 final List<Map<String, String>> rules = readRules(filter);
858 final Set<EdgeRule> result = new HashSet<>();
859 rules.forEach(item -> {
861 result.add(buildRule(item));
868 private static class Helper {
869 private static final EdgeRules INSTANCE = new EdgeRules();
870 private static final Map<Version, EdgeRules> INSTANCEMAP = new ConcurrentHashMap<>();
874 private static EdgeRules getEdgeRulesByFilename(String rulesFilename) {
875 return new EdgeRules(rulesFilename);
878 private static EdgeRules getVersionedEdgeRules(Version v) {
879 if (Version.isLatest(v)) {
882 if (!INSTANCEMAP.containsKey(v)) {
883 INSTANCEMAP.put(v, new EdgeRules(v));
885 return INSTANCEMAP.get(v);