/** * Attempts to assign an arc to a given requirement status and returns true if consistency may * still be possible */ private boolean attemptArcAssignment(Arc arc, SearchDirection direction) { // initialize search stack for roll back pushSearchStack(); // try to assign value to element and propagate changes // to other elements. If propagation fails, rollback changes try { if (logger.isDebugEnabled()) { logger.debug("Attempting move - Direction[" + direction + "], Arc[" + arc + "]"); } arc.setRequirement((direction == SearchDirection.LEFT) ? false : true); propagate(); if (logger.isDebugEnabled()) { logger.debug("Move succeeded - State after move"); dumpStateToLog(); } return true; } catch (ConsistencyException cx) { logger.debug("Move failed"); popSearchStack(); return false; } }
/** 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; } }
/** * 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"); }