/* * Copyright (c) 1998-2017 by Richard A. Wilkes. All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, version 2.0. If a copy of the MPL was not distributed with * this file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This Source Code Form is "Incompatible With Secondary Licenses", as * defined by the Mozilla Public License, version 2.0. */ package com.trollworks.gcs.prereq; import com.trollworks.gcs.character.GURPSCharacter; import com.trollworks.gcs.criteria.IntegerCriteria; import com.trollworks.gcs.criteria.NumericCompareType; import com.trollworks.gcs.widgets.outline.ListRow; import com.trollworks.toolkit.annotation.Localize; import com.trollworks.toolkit.io.xml.XMLNodeType; import com.trollworks.toolkit.io.xml.XMLReader; import com.trollworks.toolkit.io.xml.XMLWriter; import com.trollworks.toolkit.utility.Localization; import com.trollworks.toolkit.utility.text.Numbers; import java.io.IOException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; /** A prerequisite list. */ public class PrereqList extends Prereq { @Localize("{0}Requires all of:\n") @Localize(locale = "de", value = "{0}Benötigt alles von:\n") @Localize(locale = "ru", value = "{0}Требует всё из:\n") @Localize(locale = "es", value = "{0}Requiere todo lo siguiente:\n") private static String REQUIRES_ALL; @Localize("{0}Requires at least one of:\n") @Localize(locale = "de", value = "{0}Benötigt mindestens eines von:\n") @Localize(locale = "ru", value = "{0}Требует одно из:\n") @Localize(locale = "es", value = "{0}Requiere al menos uno de los siguientes:\n") private static String REQUIRES_ANY; static { Localization.initialize(); } /** The XML tag used for the prereq list. */ public static final String TAG_ROOT = "prereq_list"; //$NON-NLS-1$ private static final String TAG_WHEN_TL = "when_tl"; //$NON-NLS-1$ private static final String ATTRIBUTE_ALL = "all"; //$NON-NLS-1$ private boolean mAll; private IntegerCriteria mWhenTLCriteria; private ArrayList<Prereq> mPrereqs; /** * Creates a new prerequisite list. * * @param parent The owning prerequisite list, if any. * @param all Whether only one criteria in this list has to be met, or all of them must be met. */ public PrereqList(PrereqList parent, boolean all) { super(parent); mAll = all; mWhenTLCriteria = new IntegerCriteria(NumericCompareType.AT_LEAST, Integer.MIN_VALUE); mPrereqs = new ArrayList<>(); } /** * Loads a prerequisite list. * * @param parent The owning prerequisite list, if any. * @param reader The XML reader to load from. */ public PrereqList(PrereqList parent, XMLReader reader) throws IOException { this(parent, true); String marker = reader.getMarker(); mAll = reader.isAttributeSet(ATTRIBUTE_ALL); do { if (reader.next() == XMLNodeType.START_TAG) { String name = reader.getName(); if (TAG_WHEN_TL.equals(name)) { mWhenTLCriteria.load(reader); } else if (TAG_ROOT.equals(name)) { mPrereqs.add(new PrereqList(this, reader)); } else if (AdvantagePrereq.TAG_ROOT.equals(name)) { mPrereqs.add(new AdvantagePrereq(this, reader)); } else if (AttributePrereq.TAG_ROOT.equals(name)) { mPrereqs.add(new AttributePrereq(this, reader)); } else if (ContainedWeightPrereq.TAG_ROOT.equals(name)) { mPrereqs.add(new ContainedWeightPrereq(this, reader)); } else if (SkillPrereq.TAG_ROOT.equals(name)) { mPrereqs.add(new SkillPrereq(this, reader)); } else if (SpellPrereq.TAG_ROOT.equals(name)) { mPrereqs.add(new SpellPrereq(this, reader)); } else { reader.skipTag(name); } } } while (reader.withinMarker(marker)); } /** * Creates a clone of the specified prerequisite list. * * @param parent The new owning prerequisite list, if any. * @param prereqList The prerequisite to clone. */ public PrereqList(PrereqList parent, PrereqList prereqList) { super(parent); mAll = prereqList.mAll; mWhenTLCriteria = new IntegerCriteria(prereqList.mWhenTLCriteria); mPrereqs = new ArrayList<>(prereqList.mPrereqs.size()); for (Prereq prereq : prereqList.mPrereqs) { mPrereqs.add(prereq.clone(this)); } } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if (obj instanceof PrereqList) { PrereqList list = (PrereqList) obj; return mAll == list.mAll && mWhenTLCriteria.equals(list.mWhenTLCriteria) && mPrereqs.equals(list.mPrereqs); } return false; } @Override public int hashCode() { return super.hashCode(); } @Override public String getXMLTag() { return TAG_ROOT; } @Override public void save(XMLWriter out) { if (!mPrereqs.isEmpty()) { out.startTag(TAG_ROOT); out.writeAttribute(ATTRIBUTE_ALL, mAll); out.finishTagEOL(); if (isWhenTLEnabled(mWhenTLCriteria)) { mWhenTLCriteria.save(out, TAG_WHEN_TL); } for (Prereq prereq : mPrereqs) { prereq.save(out); } out.endTagEOL(TAG_ROOT, true); } } /** @return The character's TL criteria. */ public IntegerCriteria getWhenTLCriteria() { return mWhenTLCriteria; } /** * @param criteria The {@link IntegerCriteria} to check. * @return Whether the character's TL criteria check is enabled. */ public static boolean isWhenTLEnabled(IntegerCriteria criteria) { return criteria.getType() != NumericCompareType.AT_LEAST || criteria.getQualifier() != Integer.MIN_VALUE; } /** * @param criteria The {@link IntegerCriteria} to work on. * @param enabled Whether the character's TL criteria check is enabled. */ public static void setWhenTLEnabled(IntegerCriteria criteria, boolean enabled) { if (isWhenTLEnabled(criteria) != enabled) { criteria.setQualifier(enabled ? 0 : Integer.MIN_VALUE); } } /** @return Whether only one criteria in this list has to be met, or all of them must be met. */ public boolean requiresAll() { return mAll; } /** * @param requiresAll Whether only one criteria in this list has to be met, or all of them must * be met. */ public void setRequiresAll(boolean requiresAll) { mAll = requiresAll; } /** * @param prereq The prerequisite to work on. * @return The index of the specified prerequisite. -1 will be returned if the component isn't a * direct child. */ public int getIndexOf(Prereq prereq) { return mPrereqs.indexOf(prereq); } /** @return The number of children in this list. */ public int getChildCount() { return mPrereqs.size(); } /** @return The children of this list. */ public List<Prereq> getChildren() { return Collections.unmodifiableList(mPrereqs); } /** * Adds the specified prerequisite to this list. * * @param index The index to add the list at. * @param prereq The prerequisite to add. */ public void add(int index, Prereq prereq) { mPrereqs.add(index, prereq); } /** * Removes the specified prerequisite from this list. * * @param prereq The prerequisite to remove. */ public void remove(Prereq prereq) { if (mPrereqs.contains(prereq)) { mPrereqs.remove(prereq); prereq.mParent = null; } } @Override public boolean satisfied(GURPSCharacter character, ListRow exclude, StringBuilder builder, String prefix) { if (isWhenTLEnabled(mWhenTLCriteria)) { if (!mWhenTLCriteria.matches(Numbers.extractInteger(character.getDescription().getTechLevel(), 0, false))) { return true; } } int satisfiedCount = 0; int total = mPrereqs.size(); boolean requiresAll = requiresAll(); StringBuilder localBuilder = builder != null ? new StringBuilder() : null; for (Prereq prereq : mPrereqs) { if (prereq.satisfied(character, exclude, localBuilder, prefix)) { satisfiedCount++; } } if (localBuilder != null && localBuilder.length() > 0) { localBuilder.insert(0, "<ul>"); //$NON-NLS-1$ localBuilder.append("</ul>"); //$NON-NLS-1$ } boolean satisfied = satisfiedCount == total || !requiresAll && satisfiedCount > 0; if (!satisfied && localBuilder != null && builder != null) { builder.append(MessageFormat.format(requiresAll ? REQUIRES_ALL : REQUIRES_ANY, prefix)); builder.append(localBuilder.toString()); } return satisfied; } @Override public Prereq clone(PrereqList parent) { return new PrereqList(parent, this); } @Override public void fillWithNameableKeys(HashSet<String> set) { for (Prereq prereq : mPrereqs) { prereq.fillWithNameableKeys(set); } } @Override public void applyNameableKeys(HashMap<String, String> map) { for (Prereq prereq : mPrereqs) { prereq.applyNameableKeys(map); } } }