/** * Calculates and returns a path that satisfies the required tables list or null if one cannot be * found * * @param requiredTables Tables that are required to be in path * @return Path with smallest number of relationships to ensure all required tables are included */ public Path getPath(PathType searchTechnique, List<BusinessTable> requiredTables) { // if reset works and validity check passes, build path if (reset(requiredTables) && isValid(searchTechnique)) { logger.debug("Path determined sucessfully"); Path path = new Path(); for (Arc arc : arcs) { if (arc.isRequired()) { if (logger.isDebugEnabled()) { logger.debug("Arc selected for path: " + arc); } path.addRelationship(arc.getRelationship()); } else if (logger.isDebugEnabled()) { logger.debug( "Arc not used for path: Requirement Known[" + arc.isRequirementKnown() + "], Required[" + arc.isRequired() + "]"); } } if (logger.isDebugEnabled()) { for (Node n : nodes) { logger.debug( "Node selection state: Requirement Known[" + n.isRequirementKnown() + "], Required[" + n.isRequired() + "]"); } } if (path.size() > 0) { return path; } } return null; }
/** * Attempts to find next valid solution to the graph depending on what type of <code>PathType * </code> is desired. * * @param searchTechnique Indicates type of search that should be performed * @param prevSolution Previous solution to allow this search to continue from that point * @return The resulting solution * @throws ConsistencyException When determine that graph is impossible to satisfy */ private Solution searchForNextSolution(PathType searchTechnique, Solution prevSolution) throws ConsistencyException { // A left move equates to setting a requirement to false and a right move is equivalent to true. // Try setting to "false" first to reduce the number of tables for most searches. // For the "any relevant" search use "true" first which is quicker SearchDirection firstDirection; SearchDirection secondDirection; if (searchTechnique == PathType.ANY_RELEVANT) { firstDirection = SearchDirection.RIGHT; secondDirection = SearchDirection.LEFT; } else { firstDirection = SearchDirection.LEFT; secondDirection = SearchDirection.RIGHT; } // if this is a subsequent search after a solution was already found, we need // to return to the location where the last move in the first direction was made List<SearchDirection> searchPath = new LinkedList<SearchDirection>(); List<Arc> searchArcs = new LinkedList<Arc>(); if (prevSolution != null) { // check for situation where we have already traversed all possible paths boolean prevContainsFirstDirection = false; for (SearchDirection direction : prevSolution.searchPath) { if (direction == firstDirection) { prevContainsFirstDirection = true; break; } } if (!prevContainsFirstDirection) { return null; } ListIterator<SearchDirection> pathIter = prevSolution.searchPath.listIterator(prevSolution.searchPath.size()); // continue to move back in search path until we find an arc that can // be assigned the second direction boolean foundSecondDir = false; while (pathIter.hasPrevious() && !foundSecondDir) { // reset the search function for next search operation reset(requiredTables); propagate(); searchPath.clear(); searchArcs.clear(); // locate the last move that has an alternative while (pathIter.hasPrevious()) { SearchDirection direction = pathIter.previous(); if (direction == firstDirection) { break; } } // recreate path up to point where we can try a different direction Iterator<Arc> arcIter = prevSolution.getSearchArcs().iterator(); if (pathIter.hasPrevious()) { Iterator<SearchDirection> redoIter = prevSolution.getSearchPath().iterator(); int lastIdx = pathIter.previousIndex(); for (int idx = 0; idx <= lastIdx; idx++) { // all of these operations should work without issue SearchDirection direction = redoIter.next(); Arc arc = arcIter.next(); if (!attemptArcAssignment(arc, direction)) { throw new ConsistencyException(arc); } // add movement to newly constructed search path searchPath.add(direction); searchArcs.add(arc); } } // before any searching will begin, make sure the path we are going down shouldn't // just be skipped int rating = getRatingForCurrentState(searchTechnique); // current state isn't any better, return it as next solution if (rating >= prevSolution.getRating()) { return new Solution(arcs, rating, searchPath, searchArcs, true); } // retrieve arc which we are going to move second direction Arc arc = arcIter.next(); // if we can't move the second direction here, continue // to move back in search path until we find an arc that can // be assigned the second direction if (attemptArcAssignment(arc, secondDirection)) { // update new search path searchPath.add(secondDirection); searchArcs.add(arc); // before any searching will begin, make sure the path we are going down shouldn't // just be skipped rating = getRatingForCurrentState(searchTechnique); // current state isn't any better, return it as next solution if (rating >= prevSolution.getRating()) { return new Solution(arcs, rating, searchPath, searchArcs, true); } // set second direction flag so search will continue foundSecondDir = true; } } // if we weren't able to make another movement, there are not more solutions if (searchPath.size() == 0) { return null; } } // dump current state of graph if (logger.isDebugEnabled()) { logger.debug("-- Graph State Before Search --"); dumpStateToLog(); } // look for arcs that are not bound int rating = -1; for (Arc a : arcs) { if (!a.isRequirementKnown()) { // try the first direction if (attemptArcAssignment(a, firstDirection)) { searchPath.add(firstDirection); } else if (attemptArcAssignment( a, secondDirection)) { // if first direction fails, try the second searchPath.add(secondDirection); } else { // If arc cannot be assigned a requirement value, throw an exception throw new ConsistencyException(a); } // record arc that was altered in search path searchArcs.add(a); // make sure solution is getting better if (prevSolution != null) { rating = getRatingForCurrentState(searchTechnique); // current state isn't any better, return it as next solution if (rating >= prevSolution.getRating()) { return new Solution(arcs, rating, searchPath, searchArcs, true); } } } } // compute rating if never computed if (rating < 0) { rating = getRatingForCurrentState(searchTechnique); } // return solution to graph problem return new Solution(arcs, rating, searchPath, searchArcs, false); }