/** * Copyright (C) 2002-2012 The FreeCol Team * * This file is part of FreeCol. * * FreeCol is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * FreeCol 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 General Public License * along with FreeCol. If not, see <http://www.gnu.org/licenses/>. */ package net.sf.freecol.common.model; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * The <code>ProductionCache</code> is contains all relevant * information about the production and consumption of the * colony. This includes the production of all colony tiles and * buildings, as well as the consumption of all units, buildings and * build queues. * */ public class ProductionCache { /** * The colony whose production is being cached. The goods stored * in the colony may need to be considered in order to prevent * excess production. */ private Colony colony; private TypeCountMap<GoodsType> netProduction = new TypeCountMap<GoodsType>(); private Map<Object, ProductionInfo> productionAndConsumption = new HashMap<Object, ProductionInfo>(); private Set<GoodsType> goodsUsed = new HashSet<GoodsType>(); private boolean upToDate = false; /** * Creates a new <code>ProductionCache</code> instance. * * @param colony a <code>Colony</code> value */ public ProductionCache(Colony colony) { this.colony = colony; } /** * Updates all data structures. The method has no side effects. * */ private synchronized void update() { if (upToDate) return; // nothing to do productionAndConsumption.clear(); netProduction.clear(); goodsUsed.clear(); ProductionMap production = new ProductionMap(); for (ColonyTile colonyTile : colony.getColonyTiles()) { List<AbstractGoods> p = colonyTile.getProduction(); if (!p.isEmpty()) { production.add(p); ProductionInfo info = new ProductionInfo(); info.addProduction(p); productionAndConsumption.put(colonyTile, info); for (AbstractGoods goods : p) { goodsUsed.add(goods.getType()); netProduction.incrementCount(goods.getType().getStoredAs(), goods.getAmount()); } } } GoodsType bells = colony.getSpecification().getGoodsType("model.goods.bells"); int unitsThatUseNoBells = colony.getSpecification().getInteger("model.option.unitsThatUseNoBells"); int amount = Math.min(unitsThatUseNoBells, colony.getUnitCount()); ProductionInfo bellsInfo = new ProductionInfo(); bellsInfo.addProduction(new AbstractGoods(bells, amount)); productionAndConsumption.put(this, bellsInfo); netProduction.incrementCount(bells, amount); for (Consumer consumer : colony.getConsumers()) { Set<Modifier> modifier = consumer.getModifierSet("model.modifier.consumeOnlySurplusProduction"); List<AbstractGoods> goods = new ArrayList<AbstractGoods>(); for (AbstractGoods g : consumer.getConsumedGoods()) { goodsUsed.add(g.getType()); AbstractGoods surplus = new AbstractGoods(production.get(g.getType())); if (modifier.isEmpty()) { surplus.setAmount(surplus.getAmount() + getGoodsCount(g.getType())); } else { surplus.setAmount((int) FeatureContainer.applyModifierSet(surplus.getAmount(), null, modifier)); } goods.add(surplus); } ProductionInfo info = null; if (consumer instanceof Building) { Building building = (Building) consumer; AbstractGoods output = null; GoodsType outputType = building.getGoodsOutputType(); if (outputType != null) { goodsUsed.add(outputType); output = new AbstractGoods(production.get(outputType)); output.setAmount(output.getAmount() + getGoodsCount(outputType)); } info = building.getProductionInfo(output, goods); } else if (consumer instanceof Unit) { info = ((Unit) consumer).getProductionInfo(goods); } else if (consumer instanceof BuildQueue) { info = ((BuildQueue<?>) consumer).getProductionInfo(goods); } if (info != null) { production.add(info.getProduction()); production.remove(info.getConsumption()); for (AbstractGoods g : info.getProduction()) { netProduction.incrementCount(g.getType().getStoredAs(), g.getAmount()); } for (AbstractGoods g : info.getConsumption()) { netProduction.incrementCount(g.getType().getStoredAs(), -g.getAmount()); } productionAndConsumption.put(consumer, info); } } this.productionAndConsumption = productionAndConsumption; this.netProduction = netProduction; upToDate = true; } /** * Returns the number of goods of the given type stored in the * colony. * * @param type a <code>GoodsType</code> value * @return an <code>int</code> value */ private int getGoodsCount(GoodsType type) { return colony.getGoodsCount(type); } /** * Invalidates the production cache. This method needs to be * called whenever global production modifiers change. This might * be the case when a new {@link FoundingFather} is added, or when * the colony's production bonus changes. * */ public synchronized void invalidate() { upToDate = false; } /** * Invalidates the production cache if it produces or consumes the * given GoodsType. This method needs to be called whenever goods * are added to or removed from the colony. * * @param goodsType a <code>GoodsType</code> value */ public synchronized void invalidate(GoodsType goodsType) { if (goodsUsed.contains(goodsType)) { upToDate = false; } } /** * Returns the net production, that is the total production minus * the total consumption, of the given GoodsType. * * @param type a <code>GoodsType</code> value * @return an <code>int</code> value */ public int getNetProductionOf(GoodsType type) { update(); return netProduction.getCount(type); } /** * Returns the <code>ProductionInfo</code> for the given {@link * WorkLocation} or {@link Consumer}. * * @param object an <code>Object</code> value * @return a <code>ProductionInfo</code> value */ public ProductionInfo getProductionInfo(Object object) { update(); return productionAndConsumption.get(object); } /** * Gets a copy of the current production state. * * @return A copy of the current production state. */ public TypeCountMap<GoodsType> getProductionMap() { update(); TypeCountMap<GoodsType> result = new TypeCountMap<GoodsType>(); result.putAll(netProduction); return result; } }