package org.jabref.logic.bibtex.comparator;
import java.util.Comparator;
import java.util.Locale;
import java.util.Objects;
import org.jabref.model.entry.AuthorList;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.FieldProperty;
import org.jabref.model.entry.InternalBibtexFields;
/**
* This implementation of Comparator takes care of most of the details of sorting BibTeX entries in JabRef. It is
* structured as a node in a linked list of comparators, where each node can contain a link to a new comparator that
* decides the ordering (by recursion) if this one can't find a difference. The next node, if any, is given at
* construction time, and an arbitrary number of nodes can be included. If the entries are equal by this comparator, and
* there is no next entry, the entries' unique IDs will decide the ordering.
*/
public class EntryComparator implements Comparator<BibEntry> {
private final String sortField;
private final boolean descending;
private final boolean binary;
private final boolean numeric;
private final Comparator<BibEntry> next;
public EntryComparator(boolean binary, boolean descending, String field, Comparator<BibEntry> next) {
this.binary = binary;
this.sortField = field;
this.descending = descending;
this.next = next;
this.numeric = InternalBibtexFields.isNumeric(sortField);
}
public EntryComparator(boolean binary, boolean descending, String field) {
this.binary = binary;
this.sortField = field;
this.descending = descending;
this.next = null;
this.numeric = InternalBibtexFields.isNumeric(sortField);
}
@Override
public int compare(BibEntry e1, BibEntry e2) {
// default equals
// TODO: with the new default equals this does not only return 0 for identical objects,
// but for all objects that have the same id and same fields
if (Objects.equals(e1, e2)) {
return 0;
}
Object f1 = e1.getField(sortField).orElse(null);
Object f2 = e2.getField(sortField).orElse(null);
if (binary) {
// We just separate on set and unset fields:
if (f1 == null) {
return f2 == null ? (next == null ? idCompare(e1, e2) : next.compare(e1, e2)) : 1;
} else {
return f2 == null ? -1 : (next == null ? idCompare(e1, e2) : next.compare(e1, e2));
}
}
// If the field is author or editor, we rearrange names so they are
// sorted according to last name.
if (InternalBibtexFields.getFieldProperties(sortField).contains(FieldProperty.PERSON_NAMES)) {
if (f1 != null) {
f1 = AuthorList.fixAuthorForAlphabetization((String) f1).toLowerCase(Locale.ROOT);
}
if (f2 != null) {
f2 = AuthorList.fixAuthorForAlphabetization((String) f2).toLowerCase(Locale.ROOT);
}
} else if (sortField.equals(BibEntry.TYPE_HEADER)) {
// Sort by type.
f1 = e1.getType();
f2 = e2.getType();
} else if (numeric) {
try {
Integer i1 = Integer.parseInt((String) f1);
Integer i2 = Integer.parseInt((String) f2);
// Ok, parsing was successful. Update f1 and f2:
f1 = i1;
f2 = i2;
} catch (NumberFormatException ex) {
// Parsing failed. Give up treating these as numbers.
// TODO: should we check which of them failed, and sort based on that?
}
}
if (f2 == null) {
if (f1 == null) {
return next == null ? idCompare(e1, e2) : next.compare(e1, e2);
} else {
return -1;
}
}
if (f1 == null) { // f2 != null here automatically
return 1;
}
int result;
if ((f1 instanceof Integer) && (f2 instanceof Integer)) {
result = -((Integer) f1).compareTo((Integer) f2);
} else if (f2 instanceof Integer) {
Integer f1AsInteger = Integer.valueOf(f1.toString());
result = -f1AsInteger.compareTo((Integer) f2);
} else if (f1 instanceof Integer) {
Integer f2AsInteger = Integer.valueOf(f2.toString());
result = -((Integer) f1).compareTo(f2AsInteger);
} else {
String ours = ((String) f1).toLowerCase(Locale.ROOT);
String theirs = ((String) f2).toLowerCase(Locale.ROOT);
int comp = ours.compareTo(theirs);
result = -comp;
}
if (result != 0) {
return descending ? result : -result; // Primary sort.
}
if (next == null) {
return idCompare(e1, e2); // If still equal, we use the unique IDs.
} else {
return next.compare(e1, e2); // Secondary sort if existent.
}
}
private static int idCompare(BibEntry b1, BibEntry b2) {
return b1.getId().compareTo(b2.getId());
}
}