@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; } } } } } } }