@Override
  public void visitElement(@NonNull XmlContext context, @NonNull Element element) {
    // Traverse all child elements
    NodeList childNodes = element.getChildNodes();
    int count = childNodes.getLength();
    Map<String, LayoutNode> nodes = Maps.newHashMap();
    for (int i = 0; i < count; i++) {
      Node node = childNodes.item(i);
      if (node.getNodeType() == Node.ELEMENT_NODE) {
        LayoutNode ln = new LayoutNode((Element) node, i);
        nodes.put(ln.getNodeId(), ln);
      }
    }

    // Node map is populated, recalculate nodes sizes
    for (LayoutNode ln : nodes.values()) {
      ln.processNode(nodes);
    }
    for (LayoutNode right : nodes.values()) {
      if (!right.mLastLeft || right.skip()) {
        continue;
      }
      Set<LayoutNode> canGrowLeft = right.canGrowLeft();
      for (LayoutNode left : nodes.values()) {
        if (left == right || !left.mLastRight || left.skip() || !left.sameBucket(right)) {
          continue;
        }
        Set<LayoutNode> canGrowRight = left.canGrowRight();
        if (canGrowLeft.size() > 0 || canGrowRight.size() > 0) {
          canGrowRight.addAll(canGrowLeft);
          LayoutNode nodeToBlame = right;
          LayoutNode otherNode = left;
          if (!canGrowRight.contains(right) && canGrowRight.contains(left)) {
            nodeToBlame = left;
            otherNode = right;
          }
          context.report(
              ISSUE,
              nodeToBlame.getNode(),
              context.getLocation(nodeToBlame.getNode()),
              String.format(
                  "`%1$s` can overlap `%2$s` if %3$s %4$s due to localized text expansion",
                  nodeToBlame.getNodeId(),
                  otherNode.getNodeId(),
                  Joiner.on(", ").join(canGrowRight),
                  canGrowRight.size() > 1 ? "grow" : "grows"));
        }
      }
    }
  }
    /**
     * Process a node of a layout. Put it into one of three processing units and determine its right
     * and left neighbours.
     */
    public void processNode(@NonNull Map<String, LayoutNode> nodes) {
      if (mProcessed) {
        return;
      }
      mProcessed = true;

      if (isInvisible()
          || hasAttr(ATTR_LAYOUT_ALIGN_RIGHT)
          || hasAttr(ATTR_LAYOUT_ALIGN_END)
          || hasAttr(ATTR_LAYOUT_ALIGN_LEFT)
          || hasAttr(ATTR_LAYOUT_ALIGN_START)) {
        mBucket = Bucket.SKIP;
      } else if (hasTrueAttr(ATTR_LAYOUT_ALIGN_PARENT_TOP)) {
        mBucket = Bucket.TOP;
      } else if (hasTrueAttr(ATTR_LAYOUT_ALIGN_PARENT_BOTTOM)) {
        mBucket = Bucket.BOTTOM;
      } else {
        if (hasAttr(ATTR_LAYOUT_ABOVE) || hasAttr(ATTR_LAYOUT_BELOW)) {
          mBucket = Bucket.SKIP;
        } else {
          String[] checkAlignment = {
            ATTR_LAYOUT_ALIGN_TOP, ATTR_LAYOUT_ALIGN_BOTTOM, ATTR_LAYOUT_ALIGN_BASELINE
          };
          for (String alignment : checkAlignment) {
            String value = mNode.getAttributeNS(ANDROID_URI, alignment);
            if (!value.isEmpty()) {
              LayoutNode otherNode = nodes.get(uniformId(value));
              if (otherNode != null) {
                otherNode.processNode(nodes);
                mBucket = otherNode.mBucket;
              }
            }
          }
        }
      }
      if (mBucket == null) {
        mBucket = Bucket.TOP;
      }

      // Check relative placement
      mToLeft = findNodeByAttr(nodes, ATTR_LAYOUT_TO_START_OF);
      if (mToLeft == null) {
        mToLeft = findNodeByAttr(nodes, ATTR_LAYOUT_TO_LEFT_OF);
      }
      if (mToLeft != null) {
        mToLeft.mLastLeft = false;
        mLastRight = false;
      }
      mToRight = findNodeByAttr(nodes, ATTR_LAYOUT_TO_END_OF);
      if (mToRight == null) {
        mToRight = findNodeByAttr(nodes, ATTR_LAYOUT_TO_RIGHT_OF);
      }
      if (mToRight != null) {
        mToRight.mLastLeft = false;
        mLastRight = false;
      }

      if (hasTrueAttr(ATTR_LAYOUT_ALIGN_PARENT_END)
          || hasTrueAttr(ATTR_LAYOUT_ALIGN_PARENT_RIGHT)) {
        mLastRight = false;
      }
      if (hasTrueAttr(ATTR_LAYOUT_ALIGN_PARENT_START)
          || hasTrueAttr(ATTR_LAYOUT_ALIGN_PARENT_LEFT)) {
        mLastLeft = false;
      }
      if (mToLeft == null && mToRight == null && mLastRight && mLastLeft) {
        mLastLeft = false;
      }
    }