<commit_before>/*
 * FindBugs - Find bugs in Java programs
 * Copyright (C) 2003,2004 University of Maryland
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs;

import java.util.*;

/**
 * Compare bug instances by only those criteria which we would expect to
 * remain constant between versions.
 */
public class VersionInsensitiveBugComparator implements Comparator<BugInstance> {
	private VersionInsensitiveBugComparator() {
	}

	/**
	 * Wrapper for BugAnnotation iterators, which filters out
	 * annotations we don't care about.
	 */
	private class FilteringAnnotationIterator implements Iterator<BugAnnotation> {
		private Iterator<BugAnnotation> iter;
		private BugAnnotation next;

		public FilteringAnnotationIterator(Iterator<BugAnnotation> iter) {
			this.iter = iter;
			this.next = null;
		}

		public boolean hasNext() {
			findNext();
			return next != null;
		}

		public BugAnnotation next() {
			findNext();
			if (next == null)
				throw new NoSuchElementException();
			BugAnnotation result = next;
			next = null;
			return result;
		}

		public void remove() {
			throw new UnsupportedOperationException();
		}

		private void findNext() {
			while (next == null) {
				if (!iter.hasNext())
					break;
				BugAnnotation candidate = iter.next();
				if (!isBoring(candidate)) {
					next = candidate;
					break;
				}
			}
		}

	}

	private boolean isBoring(BugAnnotation annotation) {
		// We ignore int annotations.
		if (annotation.getClass() == IntAnnotation.class)
			return true;

		// Ignore source line annotations that aren't
		// either default or unknown.
		if (annotation instanceof SourceLineAnnotation) {
			SourceLineAnnotation srcLine = (SourceLineAnnotation) annotation;
			String description = srcLine.getDescription();
			return !(description.equals("SOURCE_LINE_DEFAULT") || description.equals("SOURCE_LINE_UNKNOWN"));
		}

		return false;
	}

	public int compare(BugInstance lhs, BugInstance rhs) {
		// Attributes of BugInstance.
		// Compare type.
		// Compare class and method annotations (ignoring line numbers).
		// Compare field annotations.

		int cmp;

		cmp = lhs.getType().compareTo(rhs.getType());
		if (cmp != 0) return cmp;

/*
		// Don't compare priority.
		// This is something that can reasonably change
		// from one release of FindBugs to anther.
		cmp = lhs.getPriority() - rhs.getPriority();
		if (cmp != 0) return cmp;
*/

		Iterator<BugAnnotation> lhsIter = new FilteringAnnotationIterator(lhs.annotationIterator());
		Iterator<BugAnnotation> rhsIter = new FilteringAnnotationIterator(rhs.annotationIterator());

		while (lhsIter.hasNext() && rhsIter.hasNext()) {
			BugAnnotation lhsAnnotation = lhsIter.next();
			BugAnnotation rhsAnnotation = rhsIter.next();

			// Different annotation types obviously cannot be equal,
			// so just compare by class name.
			if (lhsAnnotation.getClass() != rhsAnnotation.getClass())
				return lhsAnnotation.getClass().getName().compareTo(rhsAnnotation.getClass().getName());

			if (lhsAnnotation.getClass() == ClassAnnotation.class ||
			        lhsAnnotation.getClass() == MethodAnnotation.class ||
			        lhsAnnotation.getClass() == FieldAnnotation.class) {
				// ClassAnnotations, MethodAnnotations, and FieldAnnotations<commit_msg>Compare by abbreviation rather than bug type.  This is much better for comparing output between different versions.<commit_after>/*
 * FindBugs - Find bugs in Java programs
 * Copyright (C) 2003,2004 University of Maryland
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs;

import java.util.*;

/**
 * Compare bug instances by only those criteria which we would expect to
 * remain constant between versions.
 */
public class VersionInsensitiveBugComparator implements Comparator<BugInstance> {
	private VersionInsensitiveBugComparator() {
	}

	/**
	 * Wrapper for BugAnnotation iterators, which filters out
	 * annotations we don't care about.
	 */
	private class FilteringAnnotationIterator implements Iterator<BugAnnotation> {
		private Iterator<BugAnnotation> iter;
		private BugAnnotation next;

		public FilteringAnnotationIterator(Iterator<BugAnnotation> iter) {
			this.iter = iter;
			this.next = null;
		}

		public boolean hasNext() {
			findNext();
			return next != null;
		}

		public BugAnnotation next() {
			findNext();
			if (next == null)
				throw new NoSuchElementException();
			BugAnnotation result = next;
			next = null;
			return result;
		}

		public void remove() {
			throw new UnsupportedOperationException();
		}

		private void findNext() {
			while (next == null) {
				if (!iter.hasNext())
					break;
				BugAnnotation candidate = iter.next();
				if (!isBoring(candidate)) {
					next = candidate;
					break;
				}
			}
		}

	}

	private boolean isBoring(BugAnnotation annotation) {
		// We ignore int annotations.
		if (annotation.getClass() == IntAnnotation.class)
			return true;

		// Ignore source line annotations that aren't
		// either default or unknown.
		if (annotation instanceof SourceLineAnnotation) {
			SourceLineAnnotation srcLine = (SourceLineAnnotation) annotation;
			String description = srcLine.getDescription();
			return !(description.equals("SOURCE_LINE_DEFAULT") || description.equals("SOURCE_LINE_UNKNOWN"));
		}

		return false;
	}

	private static int compareNullElements(Object a, Object b) {
		if (a != null)
			return 1;
		else if (b != null)
			return -1;
		else
			return 0;
	}
	
	private static String getCode(String pattern) {
		int sep = pattern.indexOf('_');
		if (sep < 0)
			return "";
		return pattern.substring(0, sep);
	}

	public int compare(BugInstance lhs, BugInstance rhs) {
		// Attributes of BugInstance.
		// Compare abbreviation 
		// Compare class and method annotations (ignoring line numbers).
		// Compare field annotations.

		int cmp;
		
		BugPattern lhsPattern = lhs.getBugPattern();
		BugPattern rhsPattern = rhs.getBugPattern();
		
		if (lhsPattern == null || rhsPattern == null) {
			// One of the patterns is missing.
			// However, we can still accurately match by abbrev (usually) by comparing
			// the part of the type before the first '_' character.
			// This is almost always equivalent to the abbrev.
			
			String lhsCode = getCode(lhs.getType());
			String rhsCode = getCode(rhs.getType());
			
			if ((cmp = lhsCode.compareTo(rhsCode)) != 0) {
				return cmp;
			}
		} else {
			// Compare by abbrev instead of type. The specific bug type can change
			// (e.g., "definitely null" to "null on simple path").  Also, we often
			// change bug pattern types from one version of FindBugs to the next.
			//
			// Source line and field name are still matched precisely, so this shouldn't
			// cause loss of precision.
			if ((cmp = lhsPattern.getAbbrev().compareTo(rhsPattern.getAbbrev())) != 0)
				return cmp;
		}
		

/*
		// Don't compare priority.
		// This is something that can reasonably change
		// from one release of FindBugs to anther.
		cmp = lhs.getPriority() - rhs.getPriority();
		if (cmp != 0) return cmp;
*/

		Iterator<BugAnnotation> lhsIter = new FilteringAnnotationIterator(lhs.annotationIterator());
		Iterator<BugAnnotation> rhsIter = new FilteringAnnotationIterator(rhs.annotationIterator());

		while (lhsIter.hasNext() && rhsIter.hasNext()) {
			BugAnnotation lhsAnnotation = lhsIter.next();
			BugAnnotation rhsAnnotation = rhsIter.next();

			// Different annotation types obviously cannot be equal,
			// so just compare by class name.
			if (lhsAnnotation.getClass() != rhsAnnotation.getClass())
				return lhsAnnotation.getClass().getName().compareTo(rhsAnnotation.getClass().getName());

			if (lhsAnnotation.getClass() == ClassAnnotation.class ||
			        lhsAnnotation.getClass() == MethodAnnotation.class ||
			        lhsAnnotation.getClass() == FieldAnnotation.class) {
				// ClassAnnotations, MethodAnnotations, and FieldAnnotations
{'fraction_non_alphanumeric': 0.09704550355833513, 'fraction_numerical': 0.006254043562648264, 'mean_word_length': 3.672544080604534, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7489673', 'n_tokens_mistral': 2927, 'n_tokens_neox': 2489, 'n_words': 909}