From c07d860f1d90dc3961b31c87dbe637dbcd87da76 Mon Sep 17 00:00:00 2001
From: Eduardo Pedroni <e.pedroni91@gmail.com>
Date: Thu, 11 Aug 2016 23:59:10 +0200
Subject: Refactored the Card data structure to be able to do filtering
 properly

---
 src/eu/equalparts/cardbase/Cardbase.java           |   2 +-
 src/eu/equalparts/cardbase/card/Card.java          |  68 +++++++++++
 .../cardbase/card/CardSetInformation.java          |  34 ++++++
 src/eu/equalparts/cardbase/card/FullCardSet.java   |  26 +++++
 .../equalparts/cardbase/cardfield/CardField.java   |  31 +++++
 .../cardbase/cardfield/IntegerCardField.java       |  23 ++++
 .../cardbase/cardfield/StringCardField.java        |  23 ++++
 src/eu/equalparts/cardbase/cards/Card.java         |  96 ----------------
 .../cardbase/cards/CardSetInformation.java         |  34 ------
 src/eu/equalparts/cardbase/cards/FullCardSet.java  |  26 -----
 src/eu/equalparts/cardbase/cards/OldCard.java      |  95 ----------------
 src/eu/equalparts/cardbase/cli/CardbaseCLI.java    |  32 +++---
 .../cardbase/comparator/CardComparator.java        |  45 ++------
 .../cardbase/comparator/ComparatorDelegates.java   |  13 ++-
 .../containers/ReferenceCardContainer.java         |   2 +-
 .../containers/StandaloneCardContainer.java        |   2 +-
 .../cardbase/filtering/CardFiltering.java          |  30 +----
 src/eu/equalparts/cardbase/gui/CardView.java       |   2 +-
 .../equalparts/cardbase/json/CardDeserializer.java |  60 ++++++++++
 .../equalparts/cardbase/json/CardSerializer.java   | 126 +++++++++++++++++++++
 src/eu/equalparts/cardbase/json/JSON.java          |  50 ++++++++
 .../equalparts/cardbase/sorting/CardSorting.java   |   2 +-
 src/eu/equalparts/cardbase/utils/JSON.java         |  41 -------
 src/eu/equalparts/cardbase/utils/MTGUniverse.java  |  13 ++-
 24 files changed, 494 insertions(+), 382 deletions(-)
 create mode 100644 src/eu/equalparts/cardbase/card/Card.java
 create mode 100644 src/eu/equalparts/cardbase/card/CardSetInformation.java
 create mode 100644 src/eu/equalparts/cardbase/card/FullCardSet.java
 create mode 100644 src/eu/equalparts/cardbase/cardfield/CardField.java
 create mode 100644 src/eu/equalparts/cardbase/cardfield/IntegerCardField.java
 create mode 100644 src/eu/equalparts/cardbase/cardfield/StringCardField.java
 delete mode 100644 src/eu/equalparts/cardbase/cards/Card.java
 delete mode 100644 src/eu/equalparts/cardbase/cards/CardSetInformation.java
 delete mode 100644 src/eu/equalparts/cardbase/cards/FullCardSet.java
 delete mode 100644 src/eu/equalparts/cardbase/cards/OldCard.java
 create mode 100644 src/eu/equalparts/cardbase/json/CardDeserializer.java
 create mode 100644 src/eu/equalparts/cardbase/json/CardSerializer.java
 create mode 100644 src/eu/equalparts/cardbase/json/JSON.java
 delete mode 100644 src/eu/equalparts/cardbase/utils/JSON.java

(limited to 'src')

diff --git a/src/eu/equalparts/cardbase/Cardbase.java b/src/eu/equalparts/cardbase/Cardbase.java
index 90e1ffe..0fef2e7 100644
--- a/src/eu/equalparts/cardbase/Cardbase.java
+++ b/src/eu/equalparts/cardbase/Cardbase.java
@@ -8,7 +8,7 @@ import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.databind.JsonMappingException;
 
 import eu.equalparts.cardbase.containers.StandaloneCardContainer;
-import eu.equalparts.cardbase.utils.JSON;
+import eu.equalparts.cardbase.json.JSON;
 
 /**
  * Provides a variety of utility methods to interact with an optionally loaded cardbase.
diff --git a/src/eu/equalparts/cardbase/card/Card.java b/src/eu/equalparts/cardbase/card/Card.java
new file mode 100644
index 0000000..adac286
--- /dev/null
+++ b/src/eu/equalparts/cardbase/card/Card.java
@@ -0,0 +1,68 @@
+package eu.equalparts.cardbase.card;
+
+import eu.equalparts.cardbase.cardfield.IntegerCardField;
+import eu.equalparts.cardbase.cardfield.StringCardField;
+import eu.equalparts.cardbase.comparator.SpecialFields.DirtyNumber;
+import eu.equalparts.cardbase.comparator.SpecialFields.Rarity;
+
+public class Card {
+	public StringCardField name;
+	public StringCardField layout;
+	public StringCardField manaCost;
+	public IntegerCardField cmc;
+	public StringCardField type;
+	@Rarity
+	public StringCardField rarity;
+	public StringCardField text;
+	public StringCardField flavor;
+	public StringCardField artist;
+	@DirtyNumber
+	public StringCardField number;
+	@DirtyNumber
+	public StringCardField power;
+	@DirtyNumber
+	public StringCardField toughness;
+	public IntegerCardField loyalty;
+	public IntegerCardField multiverseid;
+	public StringCardField imageName;
+	public StringCardField watermark;
+
+	// Not part of upstream JSON
+	public StringCardField setCode;
+	public StringCardField imageCode;
+	
+	public static int makeHash(String setCode, String number) {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((number == null) ? 0 : number.hashCode());
+		result = prime * result + ((setCode == null) ? 0 : setCode.hashCode());
+		return result;
+	}
+	
+	@Override
+	public int hashCode() {
+		return makeHash(setCode.get(), number.get());
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		Card other = (Card) obj;
+		if (number == null) {
+			if (other.number != null)
+				return false;
+		} else if (!number.equals(other.number))
+			return false;
+		if (setCode == null) {
+			if (other.setCode != null)
+				return false;
+		} else if (!setCode.equals(other.setCode))
+			return false;
+		return true;
+	}
+}
diff --git a/src/eu/equalparts/cardbase/card/CardSetInformation.java b/src/eu/equalparts/cardbase/card/CardSetInformation.java
new file mode 100644
index 0000000..a01c18d
--- /dev/null
+++ b/src/eu/equalparts/cardbase/card/CardSetInformation.java
@@ -0,0 +1,34 @@
+package eu.equalparts.cardbase.card;
+
+public class CardSetInformation {
+
+	private String name;
+	private String code;
+	private String releaseDate;
+	
+	/**
+	 * @return the set's name.
+	 */
+	public String getName() {
+		return name;
+	}
+
+	/**
+	 * @return the set code.
+	 */
+	public String getCode() {
+		return code;
+	}
+
+	/**
+	 * @return the set's release date.
+	 */
+	public String getReleaseDate() {
+		return releaseDate;
+	}
+
+	@Override
+	public String toString() {
+		return String.format("%1$-12s : %2$s", code, name, releaseDate);
+	}
+}
diff --git a/src/eu/equalparts/cardbase/card/FullCardSet.java b/src/eu/equalparts/cardbase/card/FullCardSet.java
new file mode 100644
index 0000000..72f1e4a
--- /dev/null
+++ b/src/eu/equalparts/cardbase/card/FullCardSet.java
@@ -0,0 +1,26 @@
+package eu.equalparts.cardbase.card;
+
+import java.util.Map;
+
+public class FullCardSet {
+	
+	public String name;
+	public String code;
+	public String magicCardsInfoCode;
+	public String releaseDate;
+	public String border;
+	public String type;
+	public String block;
+	public String gathererCode;
+	public Map<String, Card> cards;
+
+	/**
+	 * Searches for a card by number (the one shown on the card itself).
+	 * 
+	 * @param number the number of the card to search.
+	 * @return the requested {@code Card}, or null if no card is found with that number.
+	 */
+	public Card getCardByNumber(String number) {
+		return cards.get(number);
+	}
+}
\ No newline at end of file
diff --git a/src/eu/equalparts/cardbase/cardfield/CardField.java b/src/eu/equalparts/cardbase/cardfield/CardField.java
new file mode 100644
index 0000000..9af775b
--- /dev/null
+++ b/src/eu/equalparts/cardbase/cardfield/CardField.java
@@ -0,0 +1,31 @@
+package eu.equalparts.cardbase.cardfield;
+import eu.equalparts.cardbase.filtering.CardFiltering.Filter;
+
+public abstract class CardField<T extends Comparable<T>> implements Comparable<CardField<T>> {
+	private String name;
+	private T value;
+	
+	public CardField(String name, T value) {
+		this.name = name;
+		this.value = value;
+	}
+	
+	public String getName() {
+		return name;
+	}
+	
+	public T get() {
+		return value;
+	}
+	
+	public void set(T newValue) {
+		this.value = newValue;
+	}
+	
+	@Override
+	public int compareTo(CardField<T> o) {
+		return value.compareTo(o.get());
+	}
+	
+	public abstract boolean filter(Filter filter, String s);
+}
diff --git a/src/eu/equalparts/cardbase/cardfield/IntegerCardField.java b/src/eu/equalparts/cardbase/cardfield/IntegerCardField.java
new file mode 100644
index 0000000..4016252
--- /dev/null
+++ b/src/eu/equalparts/cardbase/cardfield/IntegerCardField.java
@@ -0,0 +1,23 @@
+package eu.equalparts.cardbase.cardfield;
+import eu.equalparts.cardbase.filtering.CardFiltering.Filter;
+
+public class IntegerCardField extends CardField<Integer> {
+
+	public IntegerCardField(String name, Integer value) {
+		super(name, value);
+	}
+
+	@Override
+	public boolean filter(Filter filter, String s) {
+		switch (filter) {
+		case CONTAINS:
+			return get().toString().contains(s);
+		case EQUALS:
+			return get().toString().equalsIgnoreCase(s);
+		case REGEX:
+			return get().toString().matches(s);
+		default:
+			return false;
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/eu/equalparts/cardbase/cardfield/StringCardField.java b/src/eu/equalparts/cardbase/cardfield/StringCardField.java
new file mode 100644
index 0000000..059a45d
--- /dev/null
+++ b/src/eu/equalparts/cardbase/cardfield/StringCardField.java
@@ -0,0 +1,23 @@
+package eu.equalparts.cardbase.cardfield;
+import eu.equalparts.cardbase.filtering.CardFiltering.Filter;
+
+public class StringCardField extends CardField<String> {
+	
+	public StringCardField(String name, String value) {
+		super(name, value);
+	}
+
+	@Override
+	public boolean filter(Filter filter, String s) {
+		switch (filter) {
+		case CONTAINS:
+			return get().toLowerCase().contains(s.toLowerCase());
+		case EQUALS:
+			return get().equalsIgnoreCase(s);
+		case REGEX:
+			return get().matches(s);
+		default:
+			return false;
+		}
+	}
+}
diff --git a/src/eu/equalparts/cardbase/cards/Card.java b/src/eu/equalparts/cardbase/cards/Card.java
deleted file mode 100644
index 37cc13e..0000000
--- a/src/eu/equalparts/cardbase/cards/Card.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package eu.equalparts.cardbase.cards;
-
-import com.fasterxml.jackson.annotation.JsonAutoDetect;
-
-import eu.equalparts.cardbase.comparator.SpecialFields.DirtyNumber;
-import eu.equalparts.cardbase.comparator.SpecialFields.Rarity;
-
-@JsonAutoDetect
-public class Card {
-	
-	public String name;
-	public String layout;
-	public String manaCost;
-	public Integer cmc;
-	public String type;
-	@Rarity
-	public String rarity;
-	public String text;
-	public String flavor;
-	public String artist;
-	@DirtyNumber
-	public String number;
-	@DirtyNumber
-	public String power;
-	@DirtyNumber
-	public String toughness;
-	public Integer loyalty;
-	public Integer multiverseid;
-	public String imageName;
-	public String watermark;
-
-	// Not part of upstream JSON
-	public String setCode;
-	public String imageCode;
-	
-	@Override
-	public Card clone() {
-		Card clone = new Card();
-		
-		clone.name = this.name;
-		clone.layout = this.layout;
-		clone.manaCost = this.manaCost;
-		clone.cmc = this.cmc;
-		clone.type = this.type;
-		clone.rarity = this.rarity;
-		clone.text = this.text;
-		clone.flavor = this.flavor;
-		clone.artist = this.artist;
-		clone.number = this.number;
-		clone.power = this.power;
-		clone.toughness = this.toughness;
-		clone.loyalty = this.loyalty;
-		clone.multiverseid = this.multiverseid;
-		clone.imageName = this.imageName;
-		clone.watermark = this.watermark;
-		clone.setCode = this.setCode;
-		clone.imageCode = this.imageCode;
-		
-		return clone;
-	}
-
-	public static int makeHash(String setCode, String number) {
-		final int prime = 31;
-		int result = 1;
-		result = prime * result + ((number == null) ? 0 : number.hashCode());
-		result = prime * result + ((setCode == null) ? 0 : setCode.hashCode());
-		return result;
-	}
-	
-	@Override
-	public int hashCode() {
-		return makeHash(setCode, number);
-	}
-
-	@Override
-	public boolean equals(Object obj) {
-		if (this == obj)
-			return true;
-		if (obj == null)
-			return false;
-		if (getClass() != obj.getClass())
-			return false;
-		Card other = (Card) obj;
-		if (number == null) {
-			if (other.number != null)
-				return false;
-		} else if (!number.equals(other.number))
-			return false;
-		if (setCode == null) {
-			if (other.setCode != null)
-				return false;
-		} else if (!setCode.equals(other.setCode))
-			return false;
-		return true;
-	}
-}
\ No newline at end of file
diff --git a/src/eu/equalparts/cardbase/cards/CardSetInformation.java b/src/eu/equalparts/cardbase/cards/CardSetInformation.java
deleted file mode 100644
index 587c99a..0000000
--- a/src/eu/equalparts/cardbase/cards/CardSetInformation.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package eu.equalparts.cardbase.cards;
-
-public class CardSetInformation {
-
-	private String name;
-	private String code;
-	private String releaseDate;
-	
-	/**
-	 * @return the set's name.
-	 */
-	public String getName() {
-		return name;
-	}
-
-	/**
-	 * @return the set code.
-	 */
-	public String getCode() {
-		return code;
-	}
-
-	/**
-	 * @return the set's release date.
-	 */
-	public String getReleaseDate() {
-		return releaseDate;
-	}
-
-	@Override
-	public String toString() {
-		return String.format("%1$-12s : %2$s", code, name, releaseDate);
-	}
-}
diff --git a/src/eu/equalparts/cardbase/cards/FullCardSet.java b/src/eu/equalparts/cardbase/cards/FullCardSet.java
deleted file mode 100644
index d627111..0000000
--- a/src/eu/equalparts/cardbase/cards/FullCardSet.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package eu.equalparts.cardbase.cards;
-
-import java.util.Map;
-
-public class FullCardSet {
-	
-	public String name;
-	public String code;
-	public String magicCardsInfoCode;
-	public String releaseDate;
-	public String border;
-	public String type;
-	public String block;
-	public String gathererCode;
-	public Map<String, Card> cards;
-
-	/**
-	 * Searches for a card by number (the one shown on the card itself).
-	 * 
-	 * @param number the number of the card to search.
-	 * @return the requested {@code Card}, or null if no card is found with that number.
-	 */
-	public Card getCardByNumber(String number) {
-		return cards.get(number);
-	}
-}
\ No newline at end of file
diff --git a/src/eu/equalparts/cardbase/cards/OldCard.java b/src/eu/equalparts/cardbase/cards/OldCard.java
deleted file mode 100644
index daa831e..0000000
--- a/src/eu/equalparts/cardbase/cards/OldCard.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package eu.equalparts.cardbase.cards;
-
-import eu.equalparts.cardbase.comparator.SpecialFields.DirtyNumber;
-import eu.equalparts.cardbase.comparator.SpecialFields.Rarity;
-
-public class OldCard {
-	
-	public String name;
-	public String layout;
-	public String manaCost;
-	public Integer cmc;
-	public String type;
-	@Rarity
-	public String rarity;
-	public String text;
-	public String flavor;
-	public String artist;
-	@DirtyNumber
-	public String number;
-	@DirtyNumber
-	public String power;
-	@DirtyNumber
-	public String toughness;
-	public Integer loyalty;
-	public Integer multiverseid;
-	public String imageName;
-	public String watermark;
-
-	// Not part of upstream JSON
-	public String setCode;
-	public String imageCode;
-	public Integer count;
-	
-	@Override
-	public OldCard clone() {
-		OldCard clone = new OldCard();
-		
-		clone.name = this.name;
-		clone.layout = this.layout;
-		clone.manaCost = this.manaCost;
-		clone.cmc = this.cmc;
-		clone.type = this.type;
-		clone.rarity = this.rarity;
-		clone.text = this.text;
-		clone.flavor = this.flavor;
-		clone.artist = this.artist;
-		clone.number = this.number;
-		clone.power = this.power;
-		clone.toughness = this.toughness;
-		clone.loyalty = this.loyalty;
-		clone.multiverseid = this.multiverseid;
-		clone.imageName = this.imageName;
-		clone.watermark = this.watermark;
-		clone.setCode = this.setCode;
-		clone.imageCode = this.imageCode;
-		clone.count = this.count;
-		
-		return clone;
-	}
-
-	public static int makeHash(String setCode, String number) {
-		final int prime = 31;
-		int result = 1;
-		result = prime * result + ((number == null) ? 0 : number.hashCode());
-		result = prime * result + ((setCode == null) ? 0 : setCode.hashCode());
-		return result;
-	}
-	
-	@Override
-	public int hashCode() {
-		return makeHash(setCode, number);
-	}
-
-	@Override
-	public boolean equals(Object obj) {
-		if (this == obj)
-			return true;
-		if (obj == null)
-			return false;
-		if (getClass() != obj.getClass())
-			return false;
-		OldCard other = (OldCard) obj;
-		if (number == null) {
-			if (other.number != null)
-				return false;
-		} else if (!number.equals(other.number))
-			return false;
-		if (setCode == null) {
-			if (other.setCode != null)
-				return false;
-		} else if (!setCode.equals(other.setCode))
-			return false;
-		return true;
-	}
-}
\ No newline at end of file
diff --git a/src/eu/equalparts/cardbase/cli/CardbaseCLI.java b/src/eu/equalparts/cardbase/cli/CardbaseCLI.java
index 7a932d0..f86365d 100644
--- a/src/eu/equalparts/cardbase/cli/CardbaseCLI.java
+++ b/src/eu/equalparts/cardbase/cli/CardbaseCLI.java
@@ -13,9 +13,9 @@ import com.fasterxml.jackson.core.JsonParseException;
 import com.fasterxml.jackson.databind.JsonMappingException;
 
 import eu.equalparts.cardbase.Cardbase;
-import eu.equalparts.cardbase.cards.Card;
-import eu.equalparts.cardbase.cards.CardSetInformation;
-import eu.equalparts.cardbase.cards.FullCardSet;
+import eu.equalparts.cardbase.card.Card;
+import eu.equalparts.cardbase.card.CardSetInformation;
+import eu.equalparts.cardbase.card.FullCardSet;
 import eu.equalparts.cardbase.utils.MTGUniverse;
 
 /**
@@ -477,7 +477,7 @@ public final class CardbaseCLI {
 	 * TODO comment
 	 */
 	private void addCard(Card card, int count) {
-		System.out.println("Added " + count + "x " + card.name + ".");
+		System.out.println("Added " + count + "x " + card.name.get() + ".");
 		cardbase.addCard(card, count);
 		savePrompt = true;
 		lastAction = Action.ADD;
@@ -496,13 +496,13 @@ public final class CardbaseCLI {
 	private void removeCard(Card card, int count) {
 		Integer removed = cardbase.removeCard(card, count); 
 		if (removed > 0) {
-			System.out.println("Removed " + removed + "x " + card.name + ".");
+			System.out.println("Removed " + removed + "x " + card.name.get() + ".");
 			savePrompt = true;
 			lastAction = Action.REMOVE;
 			lastAction.card = card;
 			lastAction.count = removed;
 		} else {
-			System.out.println(card.name + " is not in the cardbase.");
+			System.out.println(card.name.get() + " is not in the cardbase.");
 		}
 	}
 
@@ -529,7 +529,7 @@ public final class CardbaseCLI {
 	 * @param card the card to glance.
 	 */
 	private void printGlance(Card card) {
-		System.out.println(String.format("%1$-4d %2$s (%3$s, %4$s)", cardbase.getCount(card), card.name, card.setCode, card.number));
+		System.out.println(String.format("%1$-4d %2$s (%3$s, %4$s)", cardbase.getCount(card), card.name.get(), card.setCode.get(), card.number.get()));
 	}
 
 	/**
@@ -541,16 +541,16 @@ public final class CardbaseCLI {
 	 */
 	private void printPerusal(Card card) {
 		printGlance(card);
-		if (card.type != null) System.out.println("\t" + card.type);
-		if (card.manaCost != null) System.out.println("\tCost: " + card.manaCost);
-		if (card.power != null && card.toughness != null) System.out.println("\t" + card.power + "/" + card.toughness);
-		if (card.loyalty != null) System.out.println("\tLoyalty: " + card.loyalty);
+		if (card.type.get() != null) System.out.println("\t" + card.type.get());
+		if (card.manaCost.get() != null) System.out.println("\tCost: " + card.manaCost.get());
+		if (card.power.get() != null && card.toughness.get() != null) System.out.println("\t" + card.power.get() + "/" + card.toughness.get());
+		if (card.loyalty.get() != null) System.out.println("\tLoyalty: " + card.loyalty.get());
 
-		if (card.text != null) System.out.println("\t" + card.text.replaceAll("\n", "\n\t"));
-		if (card.flavor != null) System.out.println("\t" + card.flavor.replaceAll("\n", "\n\t"));
+		if (card.text.get() != null) System.out.println("\t" + card.text.get().replaceAll("\n", "\n\t"));
+		if (card.flavor.get() != null) System.out.println("\t" + card.flavor.get().replaceAll("\n", "\n\t"));
 
-		if (card.rarity != null) System.out.println("\t" + card.rarity);
-		if (card.multiverseid != null) System.out.println("\tMID: " + card.multiverseid);
-		if (card.artist != null) System.out.println("\tIllus. " + card.artist);
+		if (card.rarity.get() != null) System.out.println("\t" + card.rarity.get());
+		if (card.multiverseid.get() != null) System.out.println("\tMID: " + card.multiverseid.get());
+		if (card.artist.get() != null) System.out.println("\tIllus. " + card.artist.get());
 	}
 }
diff --git a/src/eu/equalparts/cardbase/comparator/CardComparator.java b/src/eu/equalparts/cardbase/comparator/CardComparator.java
index c3ff736..d2cd0c1 100644
--- a/src/eu/equalparts/cardbase/comparator/CardComparator.java
+++ b/src/eu/equalparts/cardbase/comparator/CardComparator.java
@@ -2,12 +2,11 @@ package eu.equalparts.cardbase.comparator;
 
 import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
 import java.util.Comparator;
 import java.util.function.BiFunction;
 
-import eu.equalparts.cardbase.cards.Card;
+import eu.equalparts.cardbase.card.Card;
+import eu.equalparts.cardbase.cardfield.CardField;
 import eu.equalparts.cardbase.comparator.SpecialFields.DirtyNumber;
 import eu.equalparts.cardbase.comparator.SpecialFields.Rarity;
 
@@ -26,7 +25,7 @@ import eu.equalparts.cardbase.comparator.SpecialFields.Rarity;
  * @author Eduardo Pedroni
  *
  */
-@SuppressWarnings({"rawtypes", "unchecked"})
+//@SuppressWarnings({"rawtypes", "unchecked"})
 public class CardComparator implements Comparator<Card> {
 	
 	/**
@@ -36,7 +35,7 @@ public class CardComparator implements Comparator<Card> {
 	/**
 	 * The comparison delegate to use for the specified field.
 	 */
-	private BiFunction<Comparable, Comparable, Integer> comparisonDelegate = (field1, field2) -> field1.compareTo(field2);
+	private BiFunction<CardField, CardField, Integer> comparisonDelegate = (field1, field2) -> field1.compareTo(field2);
 	
 	/**
 	 * Creates a new comparator for the specified field only. This class
@@ -46,11 +45,13 @@ public class CardComparator implements Comparator<Card> {
 	 * <br>
 	 * For reference, {@code String} and {@code Integer} are both self comparable.
 	 * 
+	 * TODO comment
+	 * 
 	 * @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())) {
+				CardField.class.isAssignableFrom(fieldToCompare.getType())) {
 
 			this.fieldToCompare = fieldToCompare;
 			
@@ -74,17 +75,17 @@ public class CardComparator implements Comparator<Card> {
 		 * so we are now free to cast to comparable
 		 */
 		try {
-			Comparable field1 = (Comparable) fieldToCompare.get(o1);
-			Comparable field2 = (Comparable) fieldToCompare.get(o2);
+			CardField field1 = (CardField) fieldToCompare.get(o1);
+			CardField field2 = (CardField) fieldToCompare.get(o2);
 			
 			// if either or both fields' values are null, skip delegation altogether since delegates are not required to deal with null values
-			if (field1 == null) {
-				if (field2 == null) {
+			if (field1.get() == null) {
+				if (field2.get() == null) {
 					return 0;
 				} else {
 					return -1;
 				}
-			} else if (field2 == null) {
+			} else if (field2.get() == null) {
 				return 1;
 			} else {
 				return comparisonDelegate.apply(field1, field2);
@@ -101,26 +102,4 @@ public class CardComparator implements Comparator<Card> {
 		// fallback, this shouldn't 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;
-	}
 }
diff --git a/src/eu/equalparts/cardbase/comparator/ComparatorDelegates.java b/src/eu/equalparts/cardbase/comparator/ComparatorDelegates.java
index cede956..292cd24 100644
--- a/src/eu/equalparts/cardbase/comparator/ComparatorDelegates.java
+++ b/src/eu/equalparts/cardbase/comparator/ComparatorDelegates.java
@@ -1,5 +1,6 @@
 package eu.equalparts.cardbase.comparator;
 
+import eu.equalparts.cardbase.cardfield.CardField;
 
 final class ComparatorDelegates {
 
@@ -19,10 +20,10 @@ final class ComparatorDelegates {
 	 * @param field2 the second field to compare.
 	 * @return a value smaller than, equal to or greater than 0 according to the standard comparison convention.
 	 */
-	public static Integer compareDirtyNumber(Comparable<String> field1, Comparable<String> field2) {
+	public static Integer compareDirtyNumber(CardField<String> field1, CardField<String> field2) {
 		// this assumes that the format is uninterrupted numbers and letters
-		String number1 = ((String) field1).replaceAll("[^0-9]+", "");
-		String number2 = ((String) field2).replaceAll("[^0-9]+", "");
+		String number1 = field1.get().replaceAll("[^0-9]+", "");
+		String number2 = field2.get().replaceAll("[^0-9]+", "");
 		
 		Integer int1 = number1.matches("[0-9]+") ? Integer.parseInt(number1) : null;
 		Integer int2 = number2.matches("[0-9]+") ? Integer.parseInt(number2) : null;
@@ -48,10 +49,10 @@ final class ComparatorDelegates {
 		return letter1.compareTo(letter2);
 	}
 
-	public static Integer compareRarity(Comparable<String> field1, Comparable<String> field2) {
+	public static Integer compareRarity(CardField<String> field1, CardField<String> field2) {
 		// assign numerical values based on the different possibilities and compare those instead
-		Integer value1 = getRarityValue((String) field1);
-		Integer value2 = getRarityValue((String) field2);
+		Integer value1 = getRarityValue(field1.get());
+		Integer value2 = getRarityValue(field2.get());
 		
 		return value1.compareTo(value2);
 	}
diff --git a/src/eu/equalparts/cardbase/containers/ReferenceCardContainer.java b/src/eu/equalparts/cardbase/containers/ReferenceCardContainer.java
index 41c4224..152b6f1 100644
--- a/src/eu/equalparts/cardbase/containers/ReferenceCardContainer.java
+++ b/src/eu/equalparts/cardbase/containers/ReferenceCardContainer.java
@@ -5,7 +5,7 @@ import java.util.Map;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-import eu.equalparts.cardbase.cards.Card;
+import eu.equalparts.cardbase.card.Card;
 
 /**
  * A class which contains card quantities with absolutely no other
diff --git a/src/eu/equalparts/cardbase/containers/StandaloneCardContainer.java b/src/eu/equalparts/cardbase/containers/StandaloneCardContainer.java
index e5a514e..1c4faf6 100644
--- a/src/eu/equalparts/cardbase/containers/StandaloneCardContainer.java
+++ b/src/eu/equalparts/cardbase/containers/StandaloneCardContainer.java
@@ -7,7 +7,7 @@ import java.util.Map;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-import eu.equalparts.cardbase.cards.Card;
+import eu.equalparts.cardbase.card.Card;
 
 /**
  * TODO fix comments
diff --git a/src/eu/equalparts/cardbase/filtering/CardFiltering.java b/src/eu/equalparts/cardbase/filtering/CardFiltering.java
index 23f439d..de71f1c 100644
--- a/src/eu/equalparts/cardbase/filtering/CardFiltering.java
+++ b/src/eu/equalparts/cardbase/filtering/CardFiltering.java
@@ -1,11 +1,11 @@
 package eu.equalparts.cardbase.filtering;
 
 import java.lang.reflect.Field;
-import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 
-import eu.equalparts.cardbase.cards.Card;
+import eu.equalparts.cardbase.card.Card;
+import eu.equalparts.cardbase.cardfield.CardField;
 
 /**
  * Contains and equals are not case sensitive. Regex is.
@@ -15,32 +15,15 @@ import eu.equalparts.cardbase.cards.Card;
  */
 public class CardFiltering {
 
-	enum FilterType {
-		EQUALS {
-			@Override
-			public boolean doFilter(String value, String filter) {
-				return value.equalsIgnoreCase(filter);
-			}
-		},	CONTAINS {
-			@Override
-			public boolean doFilter(String value, String filter) {
-				return value.toLowerCase().contains(filter.toLowerCase());
-			}
-		}, REGEX {
-			@Override
-			public boolean doFilter(String value, String filter) {
-				return value.matches(filter);
-			}
-		};
-
-		public abstract boolean doFilter(String value, String filter);
+	public enum Filter {
+		EQUALS, CONTAINS, REGEX;
 	}
 
-	public static Collection<Card> filterByField(List<Card> cards, String fieldName, FilterType filterType, String filterValue) throws NoSuchFieldException {
+	public static void filterByField(List<Card> cards, String fieldName, Filter filterType, String filterValue) throws NoSuchFieldException {	
 		Field fieldToFilter = Card.class.getDeclaredField(fieldName);
 		try {
 			for (Iterator<Card> iterator = cards.iterator(); iterator.hasNext();) {
-				if (!filterType.doFilter((String) fieldToFilter.get(iterator.next()), filterValue)) {
+				if (!((CardField<?>) fieldToFilter.get(iterator.next())).filter(filterType, filterValue)) {
 					iterator.remove();
 				}
 			}
@@ -51,7 +34,6 @@ public class CardFiltering {
 			System.out.println("Error: field " + fieldToFilter.getName() + " in Card is not visible.");
 			e.printStackTrace();
 		}
-		return cards;
 	}
 
 }
diff --git a/src/eu/equalparts/cardbase/gui/CardView.java b/src/eu/equalparts/cardbase/gui/CardView.java
index 2ec7576..9774537 100644
--- a/src/eu/equalparts/cardbase/gui/CardView.java
+++ b/src/eu/equalparts/cardbase/gui/CardView.java
@@ -1,6 +1,6 @@
 package eu.equalparts.cardbase.gui;
 
-import eu.equalparts.cardbase.cards.Card;
+import eu.equalparts.cardbase.card.Card;
 import javafx.event.EventHandler;
 import javafx.scene.image.ImageView;
 import javafx.scene.input.MouseEvent;
diff --git a/src/eu/equalparts/cardbase/json/CardDeserializer.java b/src/eu/equalparts/cardbase/json/CardDeserializer.java
new file mode 100644
index 0000000..c7ca0a1
--- /dev/null
+++ b/src/eu/equalparts/cardbase/json/CardDeserializer.java
@@ -0,0 +1,60 @@
+package eu.equalparts.cardbase.json;
+
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
+
+import eu.equalparts.cardbase.card.Card;
+import eu.equalparts.cardbase.cardfield.IntegerCardField;
+import eu.equalparts.cardbase.cardfield.StringCardField;
+
+public class CardDeserializer extends StdDeserializer<Card> {
+	
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1894617997342518472L;
+
+	protected CardDeserializer(Class<?> vc) {
+		super(vc);
+	}
+
+	public CardDeserializer() {
+		this(null);
+	}
+
+	@Override
+	public Card deserialize(JsonParser jp, DeserializationContext ctxt)
+			throws IOException, JsonProcessingException {
+		JsonNode node = jp.getCodec().readTree(jp);
+		
+		Card newCard = new Card();
+		
+		newCard.name = new StringCardField("name", node.hasNonNull("name") ? node.get("name").asText() : null);
+		newCard.layout = new StringCardField("layout", node.hasNonNull("layout") ? node.get("layout").asText() : null);
+		newCard.manaCost = new StringCardField("manaCost", node.hasNonNull("manaCost") ? node.get("manaCost").asText() : null);
+		newCard.cmc = new IntegerCardField("cmc", node.hasNonNull("cmc") ? node.get("cmc").asInt() : null);
+		newCard.type = new StringCardField("type", node.hasNonNull("type") ? node.get("type").asText() : null);
+		newCard.rarity = new StringCardField("rarity", node.hasNonNull("rarity") ? node.get("rarity").asText() : null);
+		newCard.text = new StringCardField("text", node.hasNonNull("text") ? node.get("text").asText() : null);
+		newCard.flavor = new StringCardField("flavor", node.hasNonNull("flavor") ? node.get("flavor").asText() : null);
+		newCard.artist = new StringCardField("artist", node.hasNonNull("artist") ? node.get("artist").asText() : null);
+		newCard.number = new StringCardField("number", node.hasNonNull("number") ? node.get("number").asText() : null);
+		newCard.power = new StringCardField("power", node.hasNonNull("power") ? node.get("power").asText() : null);
+		newCard.toughness = new StringCardField("toughness", node.hasNonNull("toughness") ? node.get("toughness").asText() : null);
+		newCard.loyalty = new IntegerCardField("loyalty", node.hasNonNull("loyalty") ? node.get("loyalty").asInt() : null);
+		newCard.multiverseid = new IntegerCardField("multiverseid", node.hasNonNull("multiverseid") ? node.get("multiverseid").asInt() : null);
+		newCard.imageName = new StringCardField("imageName", node.hasNonNull("imageName") ? node.get("imageName").asText() : null);
+		newCard.watermark = new StringCardField("watermark", node.hasNonNull("watermark") ? node.get("watermark").asText() : null);
+		newCard.setCode = new StringCardField("setCode", node.hasNonNull("setCode") ? node.get("setCode").asText() : null);
+		newCard.imageCode = new StringCardField("imageCode", node.hasNonNull("imageCode") ? node.get("imageCode").asText() : null);
+ 
+        return newCard;
+	}
+
+}
diff --git a/src/eu/equalparts/cardbase/json/CardSerializer.java b/src/eu/equalparts/cardbase/json/CardSerializer.java
new file mode 100644
index 0000000..05b41c0
--- /dev/null
+++ b/src/eu/equalparts/cardbase/json/CardSerializer.java
@@ -0,0 +1,126 @@
+package eu.equalparts.cardbase.json;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonSerializer;
+import com.fasterxml.jackson.databind.SerializerProvider;
+
+import eu.equalparts.cardbase.card.Card;
+
+public class CardSerializer extends JsonSerializer<Card> {
+	@Override
+	public void serialize(Card card, JsonGenerator jGen, SerializerProvider serializerProvider) throws IOException  {
+		jGen.writeStartObject();
+
+		if (card.name.get() != null) {
+			jGen.writeStringField("name", card.name.get());
+		} else {
+			jGen.writeNullField("name");
+		}
+
+		if (card.layout.get() != null) {
+			jGen.writeStringField("layout", card.layout.get());
+		} else {
+			jGen.writeNullField("layout");
+		}
+
+		if (card.manaCost.get() != null) {
+			jGen.writeStringField("manaCost", card.manaCost.get());
+		} else {
+			jGen.writeNullField("manaCost");
+		}
+
+		if (card.cmc.get() != null) {
+			jGen.writeNumberField("cmc", card.cmc.get());
+		} else {
+			jGen.writeNullField("cmc");
+		}
+
+		if (card.type.get() != null) {
+			jGen.writeStringField("type", card.type.get());
+		} else {
+			jGen.writeNullField("type");
+		}
+
+		if (card.rarity.get() != null) {
+			jGen.writeStringField("rarity", card.rarity.get());
+		} else {
+			jGen.writeNullField("rarity");
+		}
+
+		if (card.text.get() != null) {
+			jGen.writeStringField("text", card.text.get());
+		} else {
+			jGen.writeNullField("text");
+		}
+
+		if (card.flavor.get() != null) {
+			jGen.writeStringField("flavor", card.flavor.get());
+		} else {
+			jGen.writeNullField("flavor");
+		}
+
+		if (card.artist.get() != null) {
+			jGen.writeStringField("artist", card.artist.get());
+		} else {
+			jGen.writeNullField("artist");
+		}
+
+		if (card.number.get() != null) {
+			jGen.writeStringField("number", card.number.get());
+		} else {
+			jGen.writeNullField("number");
+		}
+
+		if (card.power.get() != null) {
+			jGen.writeStringField("power", card.power.get());
+		} else {
+			jGen.writeNullField("power");
+		}
+
+		if (card.toughness.get() != null) {
+			jGen.writeStringField("toughness", card.toughness.get());
+		} else {
+			jGen.writeNullField("toughness");
+		}
+
+		if (card.loyalty.get() != null) {
+			jGen.writeNumberField("loyalty", card.loyalty.get());
+		} else {
+			jGen.writeNullField("loyalty");
+		}
+
+		if (card.multiverseid.get() != null) {
+			jGen.writeNumberField("multiverseid", card.multiverseid.get());
+		} else {
+			jGen.writeNullField("multiverseid");
+		}
+
+		if (card.imageName.get() != null) {
+			jGen.writeStringField("imageName", card.imageName.get());
+		} else {
+			jGen.writeNullField("imageName");
+		}
+
+		if (card.watermark.get() != null) {
+			jGen.writeStringField("watermark", card.watermark.get());
+		} else {
+			jGen.writeNullField("watermark");
+		}
+
+		if (card.setCode.get() != null) {
+			jGen.writeStringField("setCode", card.setCode.get());
+		} else {
+			jGen.writeNullField("setCode");
+		}
+
+		if (card.imageCode.get() != null) {
+			jGen.writeStringField("imageCode", card.imageCode.get());
+		} else {
+			jGen.writeNullField("imageCode");
+		}
+
+		jGen.writeEndObject();
+	}
+}
diff --git a/src/eu/equalparts/cardbase/json/JSON.java b/src/eu/equalparts/cardbase/json/JSON.java
new file mode 100644
index 0000000..b06f205
--- /dev/null
+++ b/src/eu/equalparts/cardbase/json/JSON.java
@@ -0,0 +1,50 @@
+package eu.equalparts.cardbase.json;
+
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.module.SimpleModule;
+
+import eu.equalparts.cardbase.card.Card;
+
+/**
+ * This class simply holds an {@code ObjectMapper} to be used whenever JSON must be parsed.
+ * In the future it may be removed in favour of individual mappers for each function.
+ * 
+ * @author Eduardo Pedroni
+ */
+public final class JSON {
+	
+	/**
+	 * The Jackson {@code ObjectMapper} which parses fetched JSON files.
+	 */
+	public static final ObjectMapper mapper = createMapper();
+
+	/**
+	 * Private constructor, this class is not to be instantiated.
+	 */
+	private JSON() {}
+	
+	/**
+	 * Instantiate and configure Jackson mapper statically. 
+	 * 
+	 * @return the {@code ObjectMapper}, ready to use.
+	 */
+	private static ObjectMapper createMapper() {
+		ObjectMapper objectMapper = new ObjectMapper();
+		// classes don't necessarily use all json fields
+		objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+		// disable auto detection
+		objectMapper.disable(MapperFeature.AUTO_DETECT_CREATORS,
+	            MapperFeature.AUTO_DETECT_FIELDS,
+	            MapperFeature.AUTO_DETECT_GETTERS,
+	            MapperFeature.AUTO_DETECT_IS_GETTERS);
+		
+		SimpleModule sm = new SimpleModule();
+		sm.addDeserializer(Card.class, new CardDeserializer());
+		sm.addSerializer(Card.class, new CardSerializer());
+		
+		objectMapper.registerModule(sm);
+		return objectMapper;
+	}
+}
diff --git a/src/eu/equalparts/cardbase/sorting/CardSorting.java b/src/eu/equalparts/cardbase/sorting/CardSorting.java
index e7f7c96..b8e36f1 100644
--- a/src/eu/equalparts/cardbase/sorting/CardSorting.java
+++ b/src/eu/equalparts/cardbase/sorting/CardSorting.java
@@ -2,7 +2,7 @@ package eu.equalparts.cardbase.sorting;
 
 import java.util.List;
 
-import eu.equalparts.cardbase.cards.Card;
+import eu.equalparts.cardbase.card.Card;
 import eu.equalparts.cardbase.comparator.CardComparator;
 
 public abstract class CardSorting {
diff --git a/src/eu/equalparts/cardbase/utils/JSON.java b/src/eu/equalparts/cardbase/utils/JSON.java
deleted file mode 100644
index 4a21311..0000000
--- a/src/eu/equalparts/cardbase/utils/JSON.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package eu.equalparts.cardbase.utils;
-
-import com.fasterxml.jackson.databind.DeserializationFeature;
-import com.fasterxml.jackson.databind.MapperFeature;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
-/**
- * This class simply holds an {@code ObjectMapper} to be used whenever JSON must be parsed.
- * In the future it may be removed in favour of individual mappers for each function.
- * 
- * @author Eduardo Pedroni
- */
-public final class JSON {
-	
-	/**
-	 * The Jackson {@code ObjectMapper} which parses fetched JSON files.
-	 */
-	public static final ObjectMapper mapper = createMapper();
-
-	/**
-	 * Private constructor, this class is not to be instantiated.
-	 */
-	private JSON() {}
-	
-	/**
-	 * Instantiate and configure Jackson mapper statically. 
-	 * 
-	 * @return the {@code ObjectMapper}, ready to use.
-	 */
-	private static ObjectMapper createMapper() {
-		ObjectMapper objectMapper = new ObjectMapper();
-		// classes don't necessarily use all json fields
-		objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
-		// disable auto detection
-		objectMapper.disable(MapperFeature.AUTO_DETECT_CREATORS,
-	            MapperFeature.AUTO_DETECT_FIELDS,
-	            MapperFeature.AUTO_DETECT_GETTERS,
-	            MapperFeature.AUTO_DETECT_IS_GETTERS);
-		return objectMapper;
-	}
-}
diff --git a/src/eu/equalparts/cardbase/utils/MTGUniverse.java b/src/eu/equalparts/cardbase/utils/MTGUniverse.java
index 6acb092..9412e6d 100644
--- a/src/eu/equalparts/cardbase/utils/MTGUniverse.java
+++ b/src/eu/equalparts/cardbase/utils/MTGUniverse.java
@@ -12,9 +12,10 @@ import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.JsonMappingException;
 import com.fasterxml.jackson.databind.JsonNode;
 
-import eu.equalparts.cardbase.cards.Card;
-import eu.equalparts.cardbase.cards.CardSetInformation;
-import eu.equalparts.cardbase.cards.FullCardSet;
+import eu.equalparts.cardbase.card.Card;
+import eu.equalparts.cardbase.card.CardSetInformation;
+import eu.equalparts.cardbase.card.FullCardSet;
+import eu.equalparts.cardbase.json.JSON;
 
 /**
  * Access point to the complete set of cards that exist in the
@@ -202,9 +203,9 @@ public final class MTGUniverse {
 			Map<String, Card> cardMap = new HashMap<String, Card>();
 			for (Card card : rawList) {
 				// add set code for convenience
-				card.setCode = setCode;
-				card.imageCode = imageCode;
-				cardMap.put(card.number, card);
+				card.setCode.set(setCode);
+				card.imageCode.set(imageCode);
+				cardMap.put(card.number.get(), card);
 			}
 			fcs.cards = cardMap;
 		} else {
-- 
cgit v1.2.3