/** Calculates graph validity */ private boolean isValid(PathType searchTechnique) { // remove all arcs from queue extendedNodeQueue.clear(); // initialize node queue with all nodes in graph basicNodeQueue.clear(); basicNodeQueue.addAll(nodes); try { // check for all search technique if (searchTechnique == PathType.ALL) { for (Node n : nodes) { n.setRequirement(true); } for (Arc a : arcs) { a.setRequirement(true); } } // start by making initial graph consistent propagate(); // search to test assigning a requirement setting // to tables not yet determined search(searchTechnique); return true; } catch (ConsistencyException cx) { logger.debug("failed to validate", cx); return false; } }
/** * Called whenever a target node is altered * * @param n Node that was altered */ public void graphElementChanged(GraphElement element) { List<GraphElement> searchDelta = (searchStack != null && searchStack.size() > 0) ? searchStack.getLast() : null; if (searchDelta != null) { searchDelta.add(element); } if (element instanceof Node) { Node n = (Node) element; basicNodeQueue.add(n); // for more complex arcs we need to do extended propagation checks if (n.getArcs().size() > 1) { extendedNodeQueue.add(n); } } }
/** * Performs work of propagating changes from source nodes to target nodes until consistency is * reached * * @throws ConsistencyException When current graph cannot be made consistent */ private void propagate() throws ConsistencyException { logger.debug("Beginning propagation"); // first prune all non-required nodes for (Node n : nodes) { n.prune(); } // process until queues are empty while (basicNodeQueue.size() > 0 || extendedNodeQueue.size() > 0) { // process basic node consistency enforcement because it's // faster than the extended arc propagation checks if (basicNodeQueue.size() > 0) { Node source = (Node) basicNodeQueue.remove(); // check if source node is bound to a requirement setting // before processing arcs if (source.isRequirementKnown()) { // get list of arcs originating at node List<Arc> sourceArcs = source.getArcs(); for (Arc arc : sourceArcs) { Node target = (arc.getLeft() == source) ? arc.getRight() : arc.getLeft(); // if source is not required, arc is not required and // we can try and prune the target if (source.isNotRequired()) { arc.setRequirement(false); target.prune(); } } } } else { // process extended enforcement of arc constraints on altered nodes // since we need to make sure that any node that is connected already Node source = (Node) extendedNodeQueue.remove(); // enforce arc constraints on nodes List<Arc> sourceArcs = source.getArcs(); for (Arc arc : sourceArcs) { arc.propagate(source); } } } // build required nodes list List<Node> requiredNodes = new LinkedList<Node>(); for (Node n : nodes) { if (n.isRequired()) { requiredNodes.add(n); } } // make sure all required nodes can reach one another before returning // to ensure consistency if (requiredNodes.size() > 1) { List<Node> targetList = new LinkedList<Node>(requiredNodes); Node start = requiredNodes.remove(0); if (!start.canReachAllNodes(targetList)) { logger.debug( "Arc propagation completed, but not all targets could be reached from first node"); throw new ConsistencyException(start); } } logger.debug("Propagation completed successfully"); }