aboutsummaryrefslogtreecommitdiffstats
path: root/src/eu/equalparts/cardbase/comparators
diff options
context:
space:
mode:
Diffstat (limited to 'src/eu/equalparts/cardbase/comparators')
-rw-r--r--src/eu/equalparts/cardbase/comparators/CardComparator.java102
1 files changed, 102 insertions, 0 deletions
diff --git a/src/eu/equalparts/cardbase/comparators/CardComparator.java b/src/eu/equalparts/cardbase/comparators/CardComparator.java
new file mode 100644
index 0000000..3640c8b
--- /dev/null
+++ b/src/eu/equalparts/cardbase/comparators/CardComparator.java
@@ -0,0 +1,102 @@
+package eu.equalparts.cardbase.comparators;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.Comparator;
+
+import eu.equalparts.cardbase.Cardbase;
+import eu.equalparts.cardbase.data.Card;
+
+/**
+ * I'm new to this reflection business, so bear with me.
+ * <br><br>
+ * The idea here is to avoid having to write one class
+ * for each comparable field in {@code Card}. The program
+ * can dynamically instantiate them as cards are compared
+ * by different fields.
+ * <br><br>
+ * This class uses reflection to determine if the specified
+ * field is comparable with itself upon construction, and throws
+ * an {@code IllegalArgumentException} if that is not the case.
+ *
+ * @author Eduardo Pedroni
+ *
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class CardComparator implements Comparator<Card> {
+
+ /**
+ * The field being compared.
+ */
+ private Field fieldToCompare;
+
+ /**
+ * Creates a new comparator for the specified field only. This class
+ * will only be constructed successfully if the field comes from
+ * {@code Card} and can be compared to itself (i.e. implements
+ * {@code Comparable<T>} where T is its own type.
+ * <br>
+ * For reference, {@code String} and {@code Integer} are both self comparable.
+ *
+ * @param fieldToCompare the field this comparator will use to compare cards, as declared in {@code Card}.
+ */
+ public CardComparator(Field fieldToCompare) {
+ if (fieldToCompare.getDeclaringClass().equals(Card.class) &&
+ isSelfComparable(fieldToCompare.getType())) {
+
+ this.fieldToCompare = fieldToCompare;
+ } else {
+ System.out.println(fieldToCompare.isAccessible());
+ System.out.println(fieldToCompare.getDeclaringClass().equals(Card.class));
+ System.out.println(isSelfComparable(fieldToCompare.getType()));
+ throw new IllegalArgumentException("The field provided is not valid.");
+ }
+ }
+
+ @Override
+ public int compare(Card o1, Card o2) {
+ try {
+ /*
+ * we've already checked that the field is self comparable,
+ * so we are now free to cast to whatever type it is and compare.
+ */
+ Comparable field1 = (Comparable) fieldToCompare.get(o1);
+ Comparable field2 = (Comparable) fieldToCompare.get(o2);
+
+ return field1.compareTo(field2);
+
+ } catch (IllegalArgumentException e) {
+ System.out.println("Error: class Card does not define field" + fieldToCompare.getName() + ".");
+ if (Cardbase.DEBUG) e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ System.out.println("Error: field " + fieldToCompare.getName() + " in Card is not visible.");
+ if (Cardbase.DEBUG) e.printStackTrace();
+ }
+
+ // not comparable, this should never happen
+ return 0;
+ }
+
+ /**
+ * Use reflection to determine if the specified class can be compared with itself.
+ *
+ * @param type the type to analyse.
+ * @return true if the type can be compared to itself using {@code compareTo()}, false otherwise.
+ */
+ private boolean isSelfComparable(Class<?> type) {
+
+ // go through all interfaces implemented by this class
+ for (Type implementedInterface : type.getGenericInterfaces()) {
+ // check if any parameterised interface found is "Comparable"
+ if (implementedInterface instanceof ParameterizedType) {
+ ParameterizedType genericInterface = (ParameterizedType) implementedInterface;
+ if (genericInterface.getRawType().equals(Comparable.class)) {
+ // check that the type argument of comparable is the same as the field type itself
+ return genericInterface.getActualTypeArguments()[0].equals(type);
+ }
+ }
+ }
+ return false;
+ }
+}