private void buildGraph(CategoryGraph graph) throws DaoException {
    LOG.info("building category graph");
    graph.catPages = new int[graph.catIndexes.size()][];
    graph.catParents = new int[graph.catIndexes.size()][];
    graph.catChildren = new int[graph.catIndexes.size()][];
    graph.catCosts = new double[graph.catIndexes.size()];

    Arrays.fill(graph.catPages, new int[0]);
    Arrays.fill(graph.catParents, new int[0]);
    Arrays.fill(graph.catChildren, new int[0]);

    // count reverse edges
    int totalEdges = 0;
    int numCatChildren[] = new int[graph.catIndexes.size()];
    int numCatParents[] = new int[graph.catIndexes.size()];
    int numCatPages[] = new int[graph.catIndexes.size()];

    DaoFilter filter = new DaoFilter().setLanguages(graph.language);
    for (LocalCategoryMember lcm : catDao.get(filter)) {
      int catIndex1 =
          graph.getCategoryIndex(lcm.getArticleId()); // cat index for page (probably -1)
      int catIndex2 = graph.getCategoryIndex(lcm.getCategoryId()); // cat index for cat
      if (catIndex1 >= 0 && catIndex2 >= 0) {
        numCatChildren[catIndex2]++;
        numCatParents[catIndex1]++;
      } else if (catIndex2 >= 0) {
        numCatPages[catIndex2]++;
      }
      totalEdges++;
    }

    // allocate space
    for (int i = 0; i < graph.catIndexes.size(); i++) {
      graph.catPages[i] = new int[numCatPages[i]];
      graph.catChildren[i] = new int[numCatChildren[i]];
      graph.catParents[i] = new int[numCatParents[i]];
    }

    // fill it
    for (LocalCategoryMember lcm : catDao.get(filter)) {
      int catIndex1 =
          graph.getCategoryIndex(lcm.getArticleId()); // cat index for page (probably -1)
      int catIndex2 = graph.getCategoryIndex(lcm.getCategoryId()); // cat index for cat
      if (catIndex1 >= 0 && catIndex2 >= 0) {
        graph.catChildren[catIndex2][--numCatChildren[catIndex2]] = catIndex1;
        graph.catParents[catIndex1][--numCatParents[catIndex1]] = catIndex2;
      } else if (catIndex2 >= 0) {
        graph.catPages[catIndex2][--numCatPages[catIndex2]] = lcm.getArticleId();
      }
    }

    for (int n : numCatChildren) {
      assert (n == 0);
    }
    for (int n : numCatPages) {
      assert (n == 0);
    }
    for (int n : numCatParents) {
      assert (n == 0);
    }
    LOG.info("loaded " + totalEdges + " edges in category graph");
  }