package transparent.core; import java.math.BigInteger; import java.util.HashMap; import java.util.Map.Entry; import java.util.Set; import java.util.TreeSet; import net.minidev.json.JSONArray; import net.minidev.json.JSONObject; import net.minidev.json.parser.JSONParser; import net.minidev.json.parser.ParseException; public class PriceTrigger { private static final JSONParser parser = new JSONParser(JSONParser.DEFAULT_PERMISSIVE_MODE); private int numTracks; private HashMap<Long, PriceTrack> moduleTracks; private TreeSet<PriceTrack> thresholdTracks; private HashMap<Long, TreeSet<PriceTrack>> moduleThresholdTracks; public PriceTrigger() { this.numTracks = 0; this.moduleTracks = new HashMap<Long, PriceTrack>(); this.thresholdTracks = new TreeSet<PriceTrack>(); this.moduleThresholdTracks = new HashMap<Long, TreeSet<PriceTrack>>(); } public int getNumTracks() { return numTracks; } public HashMap<Long, PriceTrack> getModuleTracks() { return moduleTracks; } public TreeSet<PriceTrack> getThresholdTracks() { return thresholdTracks; } public HashMap<Long, TreeSet<PriceTrack>> getModuleThresholdTracks() { return moduleThresholdTracks; } public synchronized void incrementTracks() { numTracks++; } public synchronized void decrementTracks() { if (numTracks > 0) numTracks--; } private static void addTrack(TreeSet<PriceTrack> thresholdTracks, PriceTrack track) { PriceTrack returned = thresholdTracks.floor(track); if (returned == null || !returned.getPrice().equals(track.getPrice())) { thresholdTracks.add(track); track.setNumTracks(1); } else { returned.incrementTracks(); } } public synchronized void addTrack(PriceTrack track) { if (track.getPrice() == null) incrementTracks(); else { PriceTrack returned = thresholdTracks.floor(track); if (returned == null || !returned.getPrice().equals(track.getPrice())) { thresholdTracks.add(track); track.setNumTracks(1); } else { returned.incrementTracks(); } } } public synchronized void addTrack(PriceTrack track, Long[] modules) { if (track.getPrice() == null) { for (Long moduleId : modules) { PriceTrack returned = moduleTracks.get(moduleId); if (returned == null) { moduleTracks.put(moduleId, track); track.setNumTracks(1); } else returned.incrementTracks(); } } else { for (Long moduleId : modules) { TreeSet<PriceTrack> set = moduleThresholdTracks.get(moduleId); if (set == null) set = new TreeSet<PriceTrack>(); addTrack(set, track); } } } private static void removeTrack(TreeSet<PriceTrack> thresholdTracks, PriceTrack track) { PriceTrack returned = thresholdTracks.floor(track); if (returned != null && returned.getPrice().equals(track.getPrice())) { returned.decrementTracks(); if (returned.getNumTracks() == 0) thresholdTracks.remove(returned); } } public synchronized void removeTrack(PriceTrack track) { if (track.getPrice() == null) decrementTracks(); else { PriceTrack returned = thresholdTracks.floor(track); if (returned != null && returned.getPrice().equals(track.getPrice())) { returned.decrementTracks(); if (returned.getNumTracks() == 0) thresholdTracks.remove(returned); } } } public synchronized void removeTrack(PriceTrack track, Long[] modules) { if (track.getPrice() == null) { for (Long module : modules) { PriceTrack returned = moduleTracks.get(module); if (returned != null && returned.getPrice() == (long) track.getPrice()) { returned.decrementTracks(); if (returned.getNumTracks() == 0) moduleTracks.remove(returned); } } } else { for (Long module : modules) { TreeSet<PriceTrack> set = moduleThresholdTracks.get(module); if (set != null) { removeTrack(set, track); if (set.isEmpty()) moduleThresholdTracks.remove(module); } } } } private static boolean checkPrice(TreeSet<PriceTrack> thresholdTracks, long price) { if (thresholdTracks == null) return false; return (thresholdTracks.ceiling(new PriceTrack(price, 0)) != null); } public synchronized boolean checkPrice(Module module, long price) { if (numTracks > 0) return true; PriceTrack track = moduleTracks.get(module.getId()); if (track != null && track.checkPrice(module, price)) return true; if (thresholdTracks.ceiling(new PriceTrack(price, 0)) != null) return true; return checkPrice(moduleThresholdTracks.get(module.getId()), price); } public static void printInfo(Set<PriceTrack> thresholds, String prefix) { if (thresholds.size() > 0) { Console.println(Console.GRAY + prefix + "thresholds: " + Console.DEFAULT); for (PriceTrack track : thresholds) Console.println(Console.GRAY + prefix + " price = " + track.getPrice() + ", count: " + Console.DEFAULT + track.getNumTracks()); } } public String save() { JSONArray array = new JSONArray(); array.add(numTracks); JSONObject object = new JSONObject(); for (Entry<Long, PriceTrack> entry : moduleTracks.entrySet()) { String key = Core.toUnsignedString(entry.getKey()); String value = entry.getValue().save(); object.put(key, value); } array.add(object); JSONArray inner = new JSONArray(); for (PriceTrack track : thresholdTracks) inner.add(track.save()); array.add(inner); object = new JSONObject(); for (Entry<Long, TreeSet<PriceTrack>> entry : moduleThresholdTracks.entrySet()) { String key = Core.toUnsignedString(entry.getKey()); inner = new JSONArray(); for (PriceTrack track : entry.getValue()) inner.add(track.save()); object.put(key, inner); } array.add(object); return array.toJSONString(); } public static PriceTrigger load(String serialized) { if (serialized == null) return null; JSONArray array; try { array = (JSONArray) parser.parse(serialized); } catch (ParseException e) { return null; } PriceTrigger trigger = new PriceTrigger(); trigger.numTracks = (int) array.get(0); JSONObject object = (JSONObject) array.get(1); for (Entry<String, Object> entry : object.entrySet()) { long key = new BigInteger(entry.getKey()).longValue(); PriceTrack value = PriceTrack.load((String) entry.getValue()); trigger.moduleTracks.put(key, value); } JSONArray inner = (JSONArray) array.get(2); for (Object obj : inner) trigger.thresholdTracks.add(PriceTrack.load((String) obj)); object = (JSONObject) array.get(3); for (Entry<String, Object> entry : object.entrySet()) { long key = new BigInteger(entry.getKey()).longValue(); TreeSet<PriceTrack> value = new TreeSet<PriceTrack>(); inner = (JSONArray) entry.getValue(); for (Object obj : inner) value.add(PriceTrack.load((String) obj)); trigger.moduleThresholdTracks.put(key, value); } return trigger; } } class PriceTrack implements Comparable<PriceTrack> { private Long price; private int numTracks; public PriceTrack(Long price) { this.price = price; this.numTracks = 0; } public PriceTrack(Long price, int numTracks) { this.price = price; this.numTracks = numTracks; } public Long getPrice() { return price; } public synchronized void incrementTracks() { numTracks++; } public synchronized void decrementTracks() { if (numTracks > 0) numTracks--; } public Integer getNumTracks() { return numTracks; } public void setNumTracks(int numTracks) { this.numTracks = numTracks; } public synchronized boolean checkPrice(Module module, long price) { return (numTracks > 0); } public String save() { return String.valueOf(price) + '.' + numTracks; } public static PriceTrack load(String serialized) { String[] tokens = serialized.split("\\."); long price = Long.parseLong(tokens[0]); int numTracks = Integer.parseInt(tokens[1]); return new PriceTrack(price, numTracks); } @Override public int compareTo(PriceTrack other) { if (price < other.price) return -1; else if (price > other.price) return 1; else return 0; } @Override public boolean equals(Object other) { if (other == this) return true; else if (other == null) return false; else if (!other.getClass().equals(this.getClass())) return false; return ((PriceTrack) other).price == this.price; } }