gt:         else if (Data.socketMap.get(socket).contains(gem))
context: package com.shadowcraft.android;
  
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.Set;
  
  import org.json.JSONArray;
  import org.json.JSONException;
  import org.json.JSONObject;
  
  import android.app.Activity;
  import android.util.Log;
  import calcs.DamageCalculator;
  public class CharHandler extends Activity{
  
      private String name;
      private String realm;
      private int gameClass;
      private int race;
      private int level;
      private String thumbnail;
      private List<Integer> professions = new ArrayList<Integer>();
      private List<String> talents = new ArrayList<String>();
      private Set<Integer> glyphs = new HashSet<Integer>();
      private Set<Integer> buffs = new HashSet<Integer>();
      private Map<String, HashMap<String, Object>> cycleSettings = new HashMap<String, HashMap<String, Object>>();
      private Map<String, Object> fightSettings = new HashMap<String, Object>();
      private JSONObject items;  // These are not final
      private Map<String, HashMap<String, Object>> itemCache;  // Info from the DB
      private Map<Integer, HashMap<String, Object>> gemCache;  // Info from the DB
      private Map<Integer, HashMap<String, Object>> enchCache;  // Info from the DB
      private Map<String, HashMap<String, Object>> charItems;  // Info from the snapshots (or bnet import)
      private DamageCalculator calculator;
      private DataBaseHelper dbHandler = getDbHandler();
  
      /**
       * Constructor. As it currently stands, this will attempt to get a stored
       * character in Bnet JSON style. Once we define how to store chars we should
       * change these constructor to function properly.
       * @param name The name of the character.
       * @param realm The realm of the character
       * @param region The region of the realm.
       */
      public CharHandler (String name, String realm, String region) {
          JSONObject cache = getCached(name, realm, region);
          if (cache == null) {
              String jsonString = Bnet.fetchChar(name, realm, region);
              JSONObject json = mkJSON(jsonString);
              try {json.get("name");}
              catch (JSONException e) {
                  System.out.println(json);
                  e.printStackTrace();
                  // TODO handle nok errors and stop execution
              }
              populateFromBnetJSON(json);
              setDefault();
              buildModeler();
          }
          else {
              populateFromSnapshot(cache);
              buildModeler();
          }
      }
  
      /**
       * Constructor overload. This will take a JSON string in the format we are
       * storing them. Note that this is to be used at run-time for now.
       * @param json Stored JSON string
       */
      public CharHandler (String json) {
          JSONObject charJSON = mkJSON(json);
          populateFromSnapshot(charJSON);
          buildModeler();
      }
  
      /**
       * Builds a string that can be parsed to JSON for easy storage of snapshots.
       * It will delete whitespace where possible. Notice that the talents field
       * needs special casing or it could be trimmed off leading zeros.
       */
      @Override
      public String toString() {
          class Strip {String strip(Object o) {
              return o.toString().replaceAll(" ", "");
          }}
          Strip s = new Strip();
  
          final StringBuilder sb = new StringBuilder();
          sb.append("{name:'").append(name).append('\'');
          sb.append(",realm:'").append(realm).append('\'');
          sb.append(",class:").append(gameClass);
          sb.append(",race:").append(race);
          sb.append(",level:").append(level);
          sb.append(",thumbnail:'").append(thumbnail).append('\'');
          sb.append(",professions:").append(s.strip(professions));
          sb.append(",glyphs:").append(s.strip(glyphs));
          sb.append(",talents:['");
          sb.append(talents.get(0)).append("','");
          sb.append(talents.get(1)).append("','");
          sb.append(talents.get(2)).append("']");
          sb.append(",items:").append(items);
          sb.append(",buffs:").append(s.strip(buffs));
          sb.append(",cycleSettings:").append(s.strip(cycleSettings));
          sb.append(",fightSettings:").append(s.strip(fightSettings));
          sb.append("}");
          return sb.toString();
      }
  
      /**
       * This should, somehow, check for cached chars (outside the app). We're
       * returning null if no cache is found (all cases for now)
       * @param name
       * @param realm
       * @param region
       * @return
       */
      public JSONObject getCached(String name, String realm, String region) {
          // define a way to retrieve cached characters.
          return null;
      }
  
      /**
       * Calls the appropriate back-end based on class
       * @return a DamageCalculator object to retrieve data from.
       */
      public DamageCalculator buildModeler() {
          if (this.isClass("rogue"))
              calculator = RogueBackend.build(this);
          else if (this.isClass(""))
              ;
          return calculator;
      }
  
      public DataBaseHelper getDbHandler() {
          dbHandler = new DataBaseHelper(this);
          dbHandler.close();
          return dbHandler;
          //try {
          //    dbHandler.openDataBase();
          //}
          //catch(SQLException sqle) {
          //    throw sqle;
          //}
      }
  
      // /////////////////////////////////////////////////////////////////////////
      // Initialize fields.
      // /////////////////////////////////////////////////////////////////////////
  
      public void populateFromBnetJSON(JSONObject charJSON) {
          try {
              name = charJSON.getString("name");
              realm = charJSON.getString("realm");
              gameClass = charJSON.getInt("class");
              race = charJSON.getInt("race");
              level = charJSON.getInt("level");
              thumbnail = charJSON.getString("thumbnail");
              items = charJSON.getJSONObject("items");
              setProfessionsFromJSON(charJSON);
              setTalentsFromJSON(charJSON);  // sets glyphs too
              setItemsFromJSON(charJSON);
              cleanItems();
          }
          catch (JSONException e) {
              e.printStackTrace();
          }
      }
  
      /**
       * Parses the objects and arrays in the JSON input into java structures.
       * @param charJSON The JSON input.
       */
      public void populateFromSnapshot(JSONObject charJSON) {
          class Deserializer {
              void intArray(Collection<Integer> field, JSONArray a)
                      throws JSONException {
                  for (int i = 0; i<a.length(); i++) {
                      field.add(a.getInt(i));
                  }
              }
              void strArray(Collection<String> field, JSONArray a)
                      throws JSONException {
                  for (int i = 0; i<a.length(); i++) {
                      field.add(a.getString(i));
                  }
              }
              void objHash(Map<String, Object> field, JSONObject o)
                      throws JSONException {
                  for(Iterator<?> it = o.keys(); it.hasNext();) {
                      String key = (String) it.next();
                      field.put(key, o.get(key));
                  }
              }
          }
          Deserializer d = new Deserializer();
  
          try {
              name = charJSON.getString("name");
              realm = charJSON.getString("realm");
              gameClass = charJSON.getInt("class");
              race = charJSON.getInt("race");
              level = charJSON.getInt("level");
              thumbnail = charJSON.getString("thumbnail");
              items = charJSON.getJSONObject("items");
              d.intArray(professions, charJSON.getJSONArray("professions"));
              d.strArray(talents, charJSON.getJSONArray("talents"));
              d.intArray(glyphs, charJSON.getJSONArray("glyphs"));
              d.intArray(buffs, charJSON.getJSONArray("buffs"));
              d.objHash(fightSettings, charJSON.getJSONObject("fightSettings"));
              JSONObject cycleSettings = charJSON.getJSONObject("cycleSettings");
              JSONObject specSettings;
              HashMap<String, Object> specSettingsMap;
              for (Iterator<?> it = cycleSettings.keys(); it.hasNext();) {
                  String spec = (String) it.next();
                  specSettings = cycleSettings.getJSONObject(spec);
                  specSettingsMap = new HashMap<String, Object>();
                  for (Iterator<?> it2 = specSettings.keys(); it2.hasNext();) {
                      String setting = (String) it2.next();
                      specSettingsMap.put(setting, specSettings.get(setting));
                  }
                  this.cycleSettings.put(spec, specSettingsMap);
              }
          }
          catch (JSONException e) {
              e.printStackTrace();
          }
      }
  
      public void setProfessionsFromJSON (JSONObject json) throws JSONException {
          JSONObject fetchedProfs = json.getJSONObject("professions");
          JSONArray primProfs = fetchedProfs.getJSONArray("primary");
          for (int i = 0; i<primProfs.length(); i++) {
              JSONObject prof = primProfs.getJSONObject(i);
              professions.add(prof.getInt("id"));
          }
      }
  
      public void setTalentsFromJSON (JSONObject json) throws JSONException {
          JSONArray talents = json.getJSONArray("talents");
          for (int i = 0; i<2; i++) {
              JSONObject build = (JSONObject) talents.get(i);
              boolean active = false;
              try {
                  active = (build.getBoolean("selected")) ? true : false;
              }
              catch (JSONException ignore) {}
              if (!active) {
                  continue;  // discard non-active build.
              }
  
              JSONObject allGlyphs = build.getJSONObject("glyphs");
              for (String s : new String[] {"prime", "minor", "major"}) {
                  JSONArray glyphs = allGlyphs.getJSONArray(s);
                  for (int j=0; j<glyphs.length(); j++) {
                      JSONObject glyph = glyphs.getJSONObject(j);
                      int a = glyph.getInt("item");
                      this.glyphs.add(a);
                  }
              }
  
              JSONArray allTrees = build.getJSONArray("trees");
              for (int j=0; j<allTrees.length(); j++) {
                  JSONObject spec = allTrees.getJSONObject(j);
                  String specString = spec.getString("points");
                  this.talents.add(specString);
              }
          }
      }
  
      public void cleanItems() {
          // TODO need to better format this field. Once we have the DB set up we
          // will start trimming this down.
          try {
              items.remove("averageItemLevel");
              items.remove("averageItemLevelEquipped");
              items.remove("tabard");
              items.remove("shirt");
  
              for (String slot : Data.itemMap.keySet()) {
                  JSONObject item = null;
                  try {
                      item = items.getJSONObject(slot);
                  }
                  catch(JSONException ignore) {
                      continue;
                  }
                  item.remove("name");
                  item.remove("icon");
                  item.remove("quality");
                  JSONObject tooltipParams = item.getJSONObject("tooltipParams");
                  tooltipParams.remove("tinker");
                  tooltipParams.remove("set");
              }
          }
          catch (JSONException ignore) {
              ignore.printStackTrace();
          }
      }
  
      /**
       * Parse the items JSON into POJO. This will put empty hashes on empty slots.
       * We use multiple try/catch since not every field is always available.
       * @param json The JSON input form Bnet
       * @throws JSONException if the input doesn't make sense.
       */
      public void setItemsFromJSON(JSONObject json) throws JSONException {
          itemCache = new HashMap<String, HashMap<String, Object>>();
          gemCache = new HashMap<Integer, HashMap<String, Object>>();
          enchCache = new HashMap<Integer, HashMap<String, Object>>();
          charItems = new HashMap<String, HashMap<String, Object>>();
  
          JSONObject items = json.getJSONObject("items");
          dbHandler.openDataBase();
          for (String slot : Data.itemMap.keySet()) {
              if (slot.equals("shirt") || slot.equals("tabard"))
                  continue;
              HashMap<String, Object> item = new HashMap<String, Object>();
              JSONObject itemJSON = items.getJSONObject(slot);
  
              // ID; not having this field implies the slot is empty, so we
              // populate with an empty hash and continue on to the next item.
              int id;
              try {
                  id = itemJSON.getInt("id");
                  item.put("id", id);
              }
              catch (JSONException e) {
                  charItems.put(slot, item);
                  continue;
              }
  
              // Extract the enchant and reforge fields if they exist.
              // Populate the enchant cache while we are at it.
              JSONObject params = itemJSON.getJSONObject("tooltipParams");
              for (String param : Arrays.asList("enchant", "reforge")) {
                  try {
                      int paramId = params.getInt(param);
                      item.put(param, paramId);
                      if (param.equals("enchant") &&
                              !enchCache.containsKey(paramId)) {
                          enchCache.put(paramId, dbHandler.getEnchant(paramId));
                      }
                  }
                  catch (JSONException ignore) {}
              }
  
              // Extract the gem fields if they exist and populate the gem cache.
              for (int i = 0; i<3; i++) {
                  try {
                      String param = "gem" + i;
                      int gemId = params.getInt(param);
                      if (!gemCache.containsKey(gemId))
                          gemCache.put(gemId, dbHandler.getGem(gemId));
                      item.put(param, params.getInt(param));
                  }
                  catch (JSONException e) {
                      continue;
                  }
              }
  
              charItems.put(slot, item);
              itemCache.put(slot, dbHandler.getItem(id));
          }
          dbHandler.close();
          Log.v("ShadowCraft", charItems.toString());
          Log.v("ShadowCraft", itemCache.toString());
          Log.v("ShadowCraft", gemCache.toString());
          Log.v("ShadowCraft", enchCache.toString());
          Log.v("ShadwoCraft", Arrays.asList(sumStats()).toString());
      }
  
      // /////////////////////////////////////////////////////////////////////////
      // These functions append the default cycle and combat settings to newly
      // created characters.
      // /////////////////////////////////////////////////////////////////////////
  
      public void setDefault() {
          if (this.isClass("rogue")) {
              rogueDefault();
          }
          else if (this.isClass("otherClass"))
              otherClassDefault();
      }
  
      public void rogueDefault() {
          int[] buffArray = {1, 2, 4, 5, 6, 9, 15, 20, 22, 26, 27, 28, 30, 31};
          for (int i : buffArray) {
              buffs.add(i);
          }
          HashMap<String, Object> assassination = new HashMap<String, Object>();
          HashMap<String, Object> combat = new HashMap<String, Object>();
          HashMap<String, Object> subtlety = new HashMap<String, Object>();
  
          assassination.put("min_envenom_size_mutilate", 4);
          assassination.put("min_envenom_size_backstab", 5);
          assassination.put("prioritize_rupture_uptime_mutilate", true);
          assassination.put("prioritize_rupture_uptime_backstab", true);
          cycleSettings.put("assassination", assassination);
  
          combat.put("use_rupture", true);
          combat.put("use_revealing_strike", "sometimes");
          combat.put("ksp_immediately", false);
          cycleSettings.put("combat", combat);
  
          subtlety.put("raid_crits_per_second", 5);
          subtlety.put("clip_recuperate", false);
          subtlety.put("use_hemorrhage", "never");
          subtlety.put("hemo_interval", 24);
          cycleSettings.put("subtlety", subtlety);
  
          fightSettings.put("time_in_execute_range", .35);
          fightSettings.put("tricks_on_cooldown", true);
          fightSettings.put("mh_poison", "ip");
          fightSettings.put("oh_poison", "dp");
          fightSettings.put("duration", 300);
          fightSettings.put("response_time", .5);
          fightSettings.put("pre_pot", false);
          fightSettings.put("combat_pot", true);
      }
  
      public void otherClassDefault () {
      }
  
      // /////////////////////////////////////////////////////////////////////////
      // Utility methods
      // /////////////////////////////////////////////////////////////////////////
  
      /**
       * Utility method to construct a JSON from a string. Use only on strings you
       * already know to be JSON: for the most part, the snapshots. The intent is
       * to skip the try/catch during development, but proper exception handling
       * should be done at some point. TODO
       * @param jsonString The string to be converted.
       * @return A JSONObject for the string.
       */
      public JSONObject mkJSON(String jsonString) {
          JSONObject json = null;
          try {
              json = new JSONObject(jsonString);
          }
          catch (JSONException e) {
              e.printStackTrace();
          }
          return json;
      }
  
      /**
       * Utility method to construct a JSONArray from a string.
       * @see mkJSON
       */
      public JSONArray mkJSONArray(String jsonString) {
          JSONArray json = null;
          try {
              json = new JSONArray(jsonString);
          }
          catch (JSONException e) {
              e.printStackTrace();
          }
          return json;
      }
  
      /**
       * Sums talents in every tree to return the spec in 31/2/8 format.
       * @return An integer array of three elements to convey the spec.
       */
      public Integer[] sumTalents() {
          String[] strings = talents();
          Integer[] spent = new Integer[3];
          for (int i = 0; i<strings.length; i++) {
              int sum = 0;
              for (int j = 0; j<strings[i].length(); j++) {
                  sum += Integer.parseInt(String.valueOf(strings[i].charAt(j)));
              }
              spent[i] = sum;
          }
          return spent;
      }
  
      /**
       * Sums talents to get the spec in which most talents are spent. Then maps
       * the result to an actual spec string.
       * @return The string of the spec (for instance "assassination")
       */
      public String specced() {
          Integer[] spent = sumTalents();
          int totalSpent = spent[0] + spent[1] + spent[2];
          int maxSpec = 0;
          int maxSpent = 0;
          for (int i = 0; i<spent.length; i++) {
              if (maxSpent < spent[i]) {
                  maxSpent = spent[i];
                  maxSpec = i;
              }
          }
          if (maxSpent < 31 && totalSpent != maxSpent)
              return null;  // TODO throw exception instead
          if (totalSpent <= 41 && totalSpent > 0)
              return Data.specMap.get(gameClass())[maxSpec];
          return Data.specMap.get(gameClass())[0];
      }
  
      /**
       * Computes the stats from gear using the charItems class field along with
       * cached items/gems from the database.
       * @return An array with stats from gear indexed by the IDs from Bnet.
       */
      @SuppressWarnings("unchecked")
      public Double[] sumStats() {
          Double[] sumStats = new Double[57];  // yes, 56 different stats total.
          boolean isBlacksmith = professions.contains(164);
          for (int i = 0; i<=56; i++) {
              sumStats[i] = 0.;
          }
          for (Entry<String, HashMap<String, Object>> e: itemCache.entrySet()) {
              String slotName = e.getKey();
              Map<String, Object> item = e.getValue();
              if (item.isEmpty())
                  continue;
              Map<String, Object> itemEquiped = charItems.get(slotName);
  
              Integer reforgeId = (Integer) itemEquiped.get("reforge");
              Integer[] reforge = Data.reforgeIDMap.get(reforgeId);
              for (Stat stat: (List<Stat>) item.get("stats")) {
                  int statId = stat.getId();
                  sumStats[statId] += stat.getValue();
                  if (reforge != null && statId == reforge[0]) {
                      int reforged = (int) (stat.getValue()) * 40 / 100;
                      sumStats[statId] -= reforged;
                      sumStats[reforge[1]] += reforged;
                  }
              }
  
              Boolean getsBonus = true;
              int gemCounter = 0;
              for (String socket : (List<String>) item.get("sockets")) {
                  Integer gemId = (Integer) itemEquiped.get("gem" + gemCounter);
                  gemCounter++;
                  if (gemId == null || !gemCache.containsKey(gemId))
                      continue;
                  HashMap<String, Object> gemData = gemCache.get(gemId);
                  String gemColor = (String) gemData.get("color");
                  boolean colorCheck = false;
                  if (Data.socketMap.containsKey(socket))
                      colorCheck = Data.socketMap.get(socket).contains(gemColor);
                  if (!(colorCheck || socket.equals(gemColor)))
                      getsBonus = false;
                  for (Stat stat: (List<Stat>) gemData.get("stats")) {
                      sumStats[stat.getId()] += stat.getValue();
                  }
              }
  
              if (slotName.equals("waist") ||
                      (isBlacksmith && slotName.equals("wrist")) ||
                      (isBlacksmith && slotName.equals("hands"))) {
                  Integer gemId = (Integer) itemEquiped.get("gem" + gemCounter);
                  if (!(gemId == null || !gemCache.containsKey(gemId))) {
                      HashMap<String, Object> gemData = gemCache.get(gemId);
                      for (Stat stat: (List<Stat>) gemData.get("stats")) {
                          sumStats[stat.getId()] += stat.getValue();
                      }
                  }
              }
  
              Stat socketBonus = (Stat) item.get("socketBonus");
              if (getsBonus && socketBonus != null) {
                  sumStats[socketBonus.getId()] += socketBonus.getValue();
              }
  
              Integer enchantId = (Integer) itemEquiped.get("enchant");
              if (enchantId != null && enchCache.containsKey(enchantId)) {
                  HashMap<String, Object> enchant = enchCache.get(enchantId);
                  for (Stat stat : (List<Stat>)enchant.get("stats")) {
                      sumStats[stat.getId()] += stat.getValue();
                  }
              }
          }
          return sumStats;
      }
  
      public boolean checkSocket(String socket, String gem) {
          if (socket.equals(gem) || socket.equals("PRISMATIC"))
              return true;
  