/**
   * Evaluates the current state of the Grid against the validation rules determined in the
   * SUDOKU_VALIDATOR_DRL and indicates if the grid is currently solved or not.
   *
   * @return true if the current state represents a completely filled out and valid Sudoku solution,
   *     false otherwise
   */
  public boolean isGridSolved() {
    boolean solved = true;

    // TODO: move this logic into SUDOKU_VALIDATOR_DRL and out of Java code
    for (int row = 0; row < NUM_ROWS; row++) {
      for (int col = 0; col < NUM_COLS; col++) {
        if (!isCellResolved(row, col)) {
          System.out.print(
              "(" + row + "," + col + ") has not been resolved but has been narrowed down to ");
          for (Integer possibleInt : getPossibleCellValues(row, col)) {
            System.out.print(possibleInt + " ");
          }
          System.out.println();
          solved = false;
        }
      }
    }

    if (solved) {
      try {
        RuleBase validatorRuleBase = DroolsUtil.getInstance().readRuleBase(SUDOKU_VALIDATOR_DRL);

        StatefulSession validatorStatefulSession = validatorRuleBase.newStatefulSession();
        List issues = new ArrayList();
        validatorStatefulSession.setGlobal("issues", issues);
        insertAllCellValues(validatorStatefulSession);
        validatorStatefulSession.fireAllRules();

        if (issues.isEmpty()) {
          System.out.println("Sucessfully Validated Solution");
        } else {
          solved = false;
          for (Object issue : issues) {
            System.out.println(issue);
          }
        }
      } catch (Exception ex) {
        ex.printStackTrace();
        throw new RuntimeException();
      }
    }

    return solved;
  }
  /**
   * Set the state of the Grid based on a two dimensional array of Integers.
   *
   * @param cellValues a two dimensional grid of Integer values for cells, a null means the value is
   *     not yet resolved
   */
  public void setCellValues(Integer[][] cellValues) {
    long startTime = System.currentTimeMillis();
    if (solverRuleBase == null) {
      try {
        solverRuleBase = DroolsUtil.getInstance().readRuleBase(SUDOKU_SOLVER_DRL);
      } catch (Exception ex) {
        ex.printStackTrace();
        throw new RuntimeException("Error Reading RuleBase for Solver");
      }
    }

    if (solverStatefulSession != null) {
      solverStatefulSession.removeEventListener(workingMemoryListener);
    }

    solverStatefulSession = solverRuleBase.newStatefulSession();
    solverStatefulSession.addEventListener(workingMemoryListener);

    for (int row = 0; row < cellValues.length; row++) {
      for (int col = 0; col < cellValues[row].length; col++) {
        cellValuesByRowAndCol[row][col] = new HashSet<Integer>();

        if (cellValues[row][col] == null) {
          for (int value = 1; value < 10; value++) {
            PossibleCellValue cellValue = new PossibleCellValue(value, row, col);
            addCellValue(cellValue);
            allCellValues.add(cellValue);
          }
        } else {
          ResolvedCellValue cellValue = new ResolvedCellValue(cellValues[row][col], row, col);
          addCellValue(cellValue);
        }
      }
    }

    insertAllCellValues(solverStatefulSession);
    System.out.println(
        "Setting up working memory and inserting all cell value POJOs took "
            + (System.currentTimeMillis() - startTime)
            + "ms.");
  }
 public FactMarshallingException(Object o, Throwable cause) {
   super("Error marshalling object: " + DroolsUtil.objectDetails(o), cause);
 }