/* * Aphelion * Copyright (c) 2013 Joris van der Wel * * This file is part of Aphelion * * Aphelion is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, version 3 of the License. * * Aphelion 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 General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Aphelion. If not, see <http://www.gnu.org/licenses/>. * * In addition, the following supplemental terms apply, based on section 7 of * the GNU Affero General Public License (version 3): * a) Preservation of all legal notices and author attributions * b) Prohibition of misrepresentation of the origin of this material, and * modified versions are required to be marked in reasonable ways as * different from the original version (for example by appending a copyright notice). * * Linking this library statically or dynamically with other modules is making a * combined work based on this library. Thus, the terms and conditions of the * GNU Affero General Public License cover the whole combination. * * As a special exception, the copyright holders of this library give you * permission to link this library with independent modules to produce an * executable, regardless of the license terms of these independent modules, * and to copy and distribute the resulting executable under terms of your * choice, provided that you also meet, for each linked independent module, * the terms and conditions of the license of that module. An independent * module is a module which is not derived from or based on this library. */ package aphelion.shared.gameconfig; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; /** * * @author Joris */ public class Selector implements Comparable<Selector>, Cloneable { private HashSet<String> weapon; private HashSet<String> ship; private HashSet<Integer> freq; private HashSet<String> perk; private int importance; private long specificity; public boolean hasWeapon() { return weapon != null; } public Set<String> getWeapon() { return weapon; } /** Set multiple matches to this selector. * These are matched to another selector as a boolean OR. * The set should not be modified after calling this method. */ public void setWeapon(HashSet<String> weapon) { this.weapon = weapon; if (weapon != null && weapon.isEmpty()) { throw new IllegalArgumentException(); } calculateSpecificity(); } public void setWeapon(String newWeapon) { if (newWeapon == null || newWeapon.length() == 0) { this.weapon = null; } else { this.weapon = new HashSet<>(1); this.weapon.add(newWeapon); } calculateSpecificity(); } public void setWeaponYaml(Object yaml) throws ClassCastException { if (yaml == null) return; if (yaml instanceof String) { this.weapon = new HashSet<>(1); this.weapon.add((String) yaml ); } else { List list = (List) yaml; if (list.size() < 1) return; this.weapon = new HashSet<>(list.size()); this.weapon.addAll(list); } calculateSpecificity(); } public boolean hasShip() { return ship != null; } public Set<String> getShip() { return ship; } /** Set multiple matches to this selector. * These are matched to another selector as a boolean OR. * The set should not be modified after calling this method. */ public void setShip(HashSet<String> ship) { this.ship = ship; if (ship != null && ship.isEmpty()) { throw new IllegalArgumentException(); } calculateSpecificity(); } public void setShip(String newShip) { if (newShip == null || newShip.length() == 0) { this.ship = null; } else { this.ship = new HashSet<>(1); this.ship.add(newShip); } calculateSpecificity(); } public void setShipYaml(Object yaml) throws ClassCastException { if (yaml == null) return; if (yaml instanceof String) { this.ship = new HashSet<>(1); this.ship.add((String) yaml ); } else { List list = (List) yaml; if (list.size() < 1) return; this.ship = new HashSet<>(list.size()); this.ship.addAll(list); } calculateSpecificity(); } public boolean hasFreq() { return freq != null; } public Set<Integer> getFreq() { return freq; } /** Set multiple matches to this selector. * These are matched to another selector as a boolean OR. * The set should not be modified after calling this method. */ public void setFreq(HashSet<Integer> freq) { this.freq = freq; if (freq != null && freq.isEmpty()) { throw new IllegalArgumentException(); } calculateSpecificity(); } public void setFreq(Integer newFreq) { if (newFreq == null) { this.freq = null; } else { this.freq = new HashSet<>(1); this.freq.add(newFreq); } calculateSpecificity(); } public void setFreqYaml(Object yaml) throws ClassCastException { if (yaml == null) return; if (yaml instanceof Integer) { this.freq = new HashSet<>(1); this.freq.add((Integer) yaml ); } else { List list = (List) yaml; if (list.size() < 1) return; this.freq = new HashSet<>(list.size()); this.freq.addAll(list); } calculateSpecificity(); } public boolean hasPerk() { return perk != null; } public Set<String> getPerk() { return perk; } /** Set multiple matches to this selector. * These are matched to another selector as a boolean OR. * The set should not be modified after calling this method. */ public void setPerk(HashSet<String> perk) { this.perk = perk; if (perk != null && perk.isEmpty()) { throw new IllegalArgumentException(); } calculateSpecificity(); } public void setPerk(String netPerk) { if (netPerk == null || netPerk.length() == 0) { this.perk = null; } else { this.perk = new HashSet<>(1); this.perk.add(netPerk); } calculateSpecificity(); } public void setPerkYaml(Object yaml) throws ClassCastException { if (yaml == null) return; if (yaml instanceof String) { this.perk = new HashSet<>(1); this.perk.add((String) yaml ); } else { List list = (List) yaml; if (list.size() < 1) return; this.perk = new HashSet<>(list.size()); this.perk.addAll(list); } calculateSpecificity(); } public int getImportance() { return importance; } public void setImportance(int importance) { this.importance = importance; calculateSpecificity(); } public void setImportance(Object yaml) throws ClassCastException { if (yaml == null) return; this.importance = (int) yaml; calculateSpecificity(); } public long getSpecificity() { return specificity; } private static final long SPECIFICITY_VALUE_BITS = 12; private static final long SPECIFICITY_MAX_VAL = (1L << SPECIFICITY_VALUE_BITS) - 1; private long spec_list(Collection list, int i) { // i want lambda ffs assert list.size() > 0; // the more it matches, the less specific it is long val = SPECIFICITY_MAX_VAL - (list.size() - 1); if (val < 0) val = 0; return val << (i * SPECIFICITY_VALUE_BITS); } private long spec_importance(long val, int i) { if (val > SPECIFICITY_MAX_VAL) { val = SPECIFICITY_MAX_VAL; } return val << (i * SPECIFICITY_VALUE_BITS); } private void calculateSpecificity() { // Least important to most important: // - weapon // - ship // - freq // - perk // - importance number // Multiple values of weapon are less important than fewer values specificity = 0; if (weapon != null) { assert !weapon.isEmpty(); specificity += spec_list(weapon, 0); } if (ship != null) { assert !ship.isEmpty(); specificity += spec_list(ship, 1); } if (freq != null) { assert !freq.isEmpty(); specificity += spec_list(freq, 2); } if (perk != null) { assert !perk.isEmpty(); specificity += spec_list(perk, 3); } if (importance > 0) { specificity += spec_importance(importance, 4); } } @Override public int compareTo(Selector o) { return Long.compare(this.specificity, o.specificity); } private static boolean containsAny(Iterable left, Collection right) { for (Object val : left) { if (right.contains(val)) { return true; } } return false; } public boolean selectorAppliesToSelection(Selector selection) { // If this selection has a weapon definition // The other selection must have atleast 1 weapon we also have. // If we do not have a weapon definition, this selection matches // every weapon or non-weapon. // same goes for the other definitions (perk, ship, etc) if (hasWeapon()) { if (!selection.hasWeapon() || !containsAny(weapon, selection.weapon)) { return false; } } if (hasShip()) { if (!selection.hasShip() || !containsAny(ship, selection.ship)) { return false; } } if (hasFreq()) { if (!selection.hasFreq() || !containsAny(freq, selection.freq)) { return false; } } if (hasPerk()) { if (!selection.hasPerk() || !containsAny(perk, selection.perk)) { return false; } } return true; } @Override public int hashCode() { int hash = 7; hash = 19 * hash + Objects.hashCode(this.weapon); hash = 19 * hash + Objects.hashCode(this.ship); hash = 19 * hash + Objects.hashCode(this.freq); hash = 19 * hash + Objects.hashCode(this.perk); hash = 19 * hash + this.importance; return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof Selector)) { return false; } final Selector other = (Selector) obj; if (!Objects.equals(this.weapon, other.weapon)) { return false; } if (!Objects.equals(this.ship, other.ship)) { return false; } if (!Objects.equals(this.freq, other.freq)) { return false; } if (!Objects.equals(this.perk, other.perk)) { return false; } if (this.importance != other.importance) { return false; } return true; } @Override protected Selector clone() throws CloneNotSupportedException { Selector ret = (Selector) super.clone(); if (weapon != null) { ret.weapon = (HashSet<String>) weapon.clone(); } if (ship != null) { ret.ship = (HashSet<String>) ship.clone(); } if (freq != null) { ret.freq = (HashSet<Integer>) freq.clone(); } if (perk != null) { ret.perk = (HashSet<String>) perk.clone(); } return ret; } public void set(Selector other) { weapon = other.weapon; ship = other.ship; freq = other.freq; perk = other.perk; importance = other.importance; specificity = other.specificity; if (weapon != null) { weapon = (HashSet<String>) weapon.clone(); } if (ship != null) { ship = (HashSet<String>) ship.clone(); } if (freq != null) { freq = (HashSet<Integer>) freq.clone(); } if (perk != null) { perk = (HashSet<String>) perk.clone(); } } @Override public String toString() { return "Selector{" + "weapon=" + weapon + ", ship=" + ship + ", freq=" + freq + ", perk=" + perk + ", importance=" + importance + ", specificity=" + specificity + '}'; } }