@Override
  public void visitElement(@NonNull XmlContext context, @NonNull Element element) {
    // Record include graph such that we can look for inter-layout duplicates after the
    // project has been fully checked

    String layout = element.getAttribute(ATTR_LAYOUT); // NOTE: Not in android: namespace
    if (layout.startsWith(LAYOUT_RESOURCE_PREFIX)) { // Ignore @android:layout/ layouts
      layout = layout.substring(LAYOUT_RESOURCE_PREFIX.length());

      if (context.getPhase() == 1) {
        if (!context.getProject().getReportIssues()) {
          // If this is a library project not being analyzed, ignore it
          return;
        }

        List<String> to = mIncludes.get(context.file);
        if (to == null) {
          to = new ArrayList<String>();
          mIncludes.put(context.file, to);
        }
        to.add(layout);
      } else {
        assert context.getPhase() == 2;

        Collection<Multimap<String, Occurrence>> maps = mLocations.get(context.file);
        if (maps != null && !maps.isEmpty()) {
          for (Multimap<String, Occurrence> map : maps) {
            if (!maps.isEmpty()) {
              Collection<Occurrence> occurrences = map.get(layout);
              if (occurrences != null && !occurrences.isEmpty()) {
                for (Occurrence occurrence : occurrences) {
                  Location location = context.getLocation(element);
                  location.setClientData(element);
                  location.setMessage(occurrence.message);
                  location.setSecondary(occurrence.location);
                  occurrence.location = location;
                }
              }
            }
          }
        }
      }
    }
  }
  @Override
  public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) {
    assert attribute.getName().equals(ATTR_ID) || attribute.getLocalName().equals(ATTR_ID);
    String id = attribute.getValue();
    if (context.getPhase() == 1) {
      if (mIds.contains(id)) {
        Location location = context.getLocation(attribute);

        Attr first = findIdAttribute(attribute.getOwnerDocument(), id);
        if (first != null && first != attribute) {
          Location secondLocation = context.getLocation(first);
          secondLocation.setMessage(String.format("%1$s originally defined here", id));
          location.setSecondary(secondLocation);
        }

        context.report(
            WITHIN_LAYOUT,
            attribute,
            location,
            String.format("Duplicate id %1$s, already defined earlier in this layout", id),
            null);
      } else if (id.startsWith(NEW_ID_PREFIX)) {
        // Skip id's on include tags
        if (attribute.getOwnerElement().getTagName().equals(VIEW_INCLUDE)) {
          return;
        }

        mIds.add(id);
      }
    } else {
      Collection<Multimap<String, Occurrence>> maps = mLocations.get(context.file);
      if (maps != null && !maps.isEmpty()) {
        for (Multimap<String, Occurrence> map : maps) {
          if (!maps.isEmpty()) {
            Collection<Occurrence> occurrences = map.get(id);
            if (occurrences != null && !occurrences.isEmpty()) {
              for (Occurrence occurrence : occurrences) {
                if (context.getDriver().isSuppressed(CROSS_LAYOUT, attribute)) {
                  return;
                }
                Location location = context.getLocation(attribute);
                location.setClientData(attribute);
                location.setMessage(occurrence.message);
                location.setSecondary(occurrence.location);
                occurrence.location = location;
              }
            }
          }
        }
      }
    }
  }