code: public JoinNode invert()
      {
          List<JoinNode> toInvert = new ArrayList();
          JoinNode cur = this;
          while(!cur.isEndNode)
          {
              toInvert.add(cur);
              cur = cur.getFurtherJoins().get(0);
          }
          // cur is now the ultimate end node
          JoinNode inverted = new JoinNode(cur.left, true);
          inverted.furtherJoins.addAll(cur.furtherJoins);
          JoinNode curInverted = inverted;
          for(int i=toInvert.size() - 1; i >= 0; i--)
          {
              JoinNode nextToInvert = toInvert.get(i);
              JoinNode nextInverted = new JoinNode(nextToInvert.left, false);
              if (nextToInvert.furtherJoins.size() > 1)
              {
                  nextInverted.furtherJoins.addAll(nextToInvert.furtherJoins.subList(1, nextToInvert.furtherJoins.size()));
              }
              nextInverted.parentJoin = cur.parentJoin.getReverseJoin();
              curInverted.furtherJoins.add(0, nextInverted);
              curInverted = nextInverted;
              cur = nextToInvert;
          }
          curInverted.isEndNode = true;
          return inverted;
      }
docs: /*
  an inverted tree inverts the core relationship path, but leaves the dangling paths alone.
  Legend: S = start, E = end, D = dangling (existence) clause, M = middle join (e.g. many-to-many)
  
  S                 E
  /   becomes       /
  E                 S
  
  S                 E
  /   becomes       /
  M                 M
  /                 /
  E                 S
  
  S                 E                                                               S
  /   becomes       / \  Note: the initial tree in memory is really setup as:       /
  E                 S   D       but E has the isEndNode flag set on it              E
  \                                                                               /
  D                                                                             D
  
  S                   E
  / \   becomes       /     Note: again, the result tree has isEndNode set on the new S node.
  E   D               S
  \
  D
  
  S                 E
  /   becomes       /
  M                 M
  / \               / \
  E   D             S   D