public void parse() {
    Stack<Node> nodeStack = new Stack();
    nodeStack.push(this.rootNode);

    for (String key : this.orgPaths.keySet()) {
      String altName = this.orgPaths.get(key);
      Log.d(LT, "Parsing: " + key);
      // if file is encrypted just add a placeholder node to be parsed later
      if (key.endsWith(".gpg") || key.endsWith(".pgp") || key.endsWith(".enc")) {
        Node nnode = new Node(key, true);
        nnode.altNodeTitle = altName;
        nnode.setParentNode(nodeStack.peek());
        nnode.addProperty("ID", this.getNodePath(nnode));
        nodeStack.peek().addChildNode(nnode);
        continue;
      }

      Node fileNode = new Node(key, false);
      fileNode.setParentNode(nodeStack.peek());
      fileNode.addProperty("ID", this.getNodePath(fileNode));
      fileNode.altNodeTitle = altName;
      nodeStack.peek().addChildNode(fileNode);
      nodeStack.push(fileNode);
      parse(fileNode, null);
      nodeStack.pop();
    }
  }
  public void parse(Node fileNode, BufferedReader breader) {
    Pattern editTitlePattern = Pattern.compile("F\\((edit:.*?)\\) \\[\\[(.*?)\\]\\[(.*?)\\]\\]");
    try {
      this.todos = appdb.getTodos();

      String thisLine;
      Stack<Node> nodeStack = new Stack();
      Pattern propertiesLine = Pattern.compile("^\\s*:[A-Z]+:");
      if (breader == null) {
        breader = this.getHandle(fileNode.nodeName);
      }
      nodeStack.push(fileNode);
      int nodeDepth = 0;

      while ((thisLine = breader.readLine()) != null) {
        int numstars = 0;
        Matcher editm = editTitlePattern.matcher(thisLine);
        if (thisLine.length() < 1 || editm.find()) continue;
        if (thisLine.charAt(0) == '#') {
          if (thisLine.indexOf("#+TITLE:") != -1) {
            fileNode.altNodeTitle = thisLine.substring(thisLine.indexOf("#+TITLE:") + 8).trim();
          }
        }
        for (int idx = 0; idx < thisLine.length(); idx++) {
          if (thisLine.charAt(idx) != '*') {
            break;
          }
          numstars++;
        }

        if (numstars >= thisLine.length() || thisLine.charAt(numstars) != ' ') {
          numstars = 0;
        }

        // headings
        if (numstars > 0) {
          String title = thisLine.substring(numstars + 1);
          TitleComponents titleComp = parseTitle(this.stripTitle(title));
          Node newNode = new Node(titleComp.title);
          newNode.setFullTitle(this.stripTitle(title));
          newNode.todo = titleComp.todo;
          newNode.priority = titleComp.priority;
          newNode.tags.addAll(titleComp.tags);
          if (numstars > nodeDepth) {
            try {
              Node lastNode = nodeStack.peek();
              newNode.setParentNode(lastNode);
              newNode.nodeId = this.getNodePath(newNode);
              newNode.addProperty("ID", newNode.nodeId);
              lastNode.addChildNode(newNode);
            } catch (EmptyStackException e) {
            }
            nodeStack.push(newNode);
            nodeDepth++;
          } else if (numstars == nodeDepth) {
            nodeStack.pop();
            nodeStack.peek().addChildNode(newNode);
            newNode.setParentNode(nodeStack.peek());
            newNode.nodeId = this.getNodePath(newNode);
            newNode.addProperty("ID", newNode.nodeId);
            nodeStack.push(newNode);
          } else if (numstars < nodeDepth) {
            for (; numstars <= nodeDepth; nodeDepth--) {
              nodeStack.pop();
            }

            Node lastNode = nodeStack.peek();
            newNode.setParentNode(lastNode);
            newNode.nodeId = this.getNodePath(newNode);
            newNode.addProperty("ID", newNode.nodeId);
            lastNode.addChildNode(newNode);
            nodeStack.push(newNode);
            nodeDepth++;
          }
        }
        // content
        else {
          Matcher propm = propertiesLine.matcher(thisLine);
          Node lastNode = nodeStack.peek();
          if (thisLine.indexOf(":ID:") != -1) {
            String trimmedLine = thisLine.substring(thisLine.indexOf(":ID:") + 4).trim();
            lastNode.addProperty("ID", trimmedLine);
            lastNode.nodeId = trimmedLine;
            continue;
          } else if (thisLine.indexOf(":ORIGINAL_ID:") != -1) {
            String trimmedLine = thisLine.substring(thisLine.indexOf(":ORIGINAL_ID:") + 13).trim();
            lastNode.addProperty("ORIGINAL_ID", trimmedLine);
            lastNode.nodeId = trimmedLine;
            continue;
          } else if (propm.find()) {
            continue;
          } else if (thisLine.indexOf("DEADLINE:") != -1 || thisLine.indexOf("SCHEDULED:") != -1) {
            try {
              Pattern deadlineP = Pattern.compile("^.*(DEADLINE: <.+?>)");
              Matcher deadlineM = deadlineP.matcher(thisLine);
              Pattern schedP = Pattern.compile("^.*(SCHEDULED: <.+?>)");
              Matcher schedM = schedP.matcher(thisLine);
              SimpleDateFormat dFormatter = new SimpleDateFormat("'DEADLINE': <yyyy-MM-dd EEE>");
              SimpleDateFormat sFormatter = new SimpleDateFormat("'SCHEDULED': <yyyy-MM-dd EEE>");
              if (deadlineM.find()) {
                lastNode.deadline = dFormatter.parse(deadlineM.group(1));
              }
              if (schedM.find()) {
                lastNode.schedule = sFormatter.parse(schedM.group(1));
              }
            } catch (java.text.ParseException e) {
              Log.e(LT, "Could not parse deadline");
            }
            continue;
          }
          lastNode.addPayload(thisLine);
        }
      }
      for (; nodeDepth > 0; nodeDepth--) {
        nodeStack.pop();
      }
      fileNode.parsed = true;
      breader.close();
    } catch (IOException e) {
      Log.e(LT, "IO Exception on readerline: " + e.getMessage());
    }
  }