/*
* Copyright (c) 2008 Tom Parker <thpr@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This program 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 Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package pcgen.cdom.helper;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import pcgen.cdom.base.CDOMObject;
import pcgen.cdom.base.CDOMReference;
import pcgen.cdom.base.ConcretePrereqObject;
import pcgen.cdom.base.Constants;
import pcgen.cdom.reference.ReferenceUtilities;
import pcgen.core.Equipment;
/**
* An AbstractProfProvider is an object that contains the ability to contain
* Proficiencies, either by TYPE of Equipment or direct references. Explicit
* Storage of TYPE vs. primitive is necessary due to the ability of the TYPE
* being a resolved against Equipment.
*
* @param <T>
* The type of Proficiency (CDOMObject) that this
* AbstractProfProvider provides
*/
public abstract class AbstractProfProvider<T extends CDOMObject> extends
ConcretePrereqObject implements ProfProvider<T>
{
/**
* Contains the set of primitive proficiencies objects that this
* AbstractProfProvider grants
*/
private final Set<CDOMReference<T>> direct;
/**
* Contains the set of TYPEs of Equipment objects for which this
* AbstractProfProvider grants proficiency
*/
private final Set<CDOMReference<Equipment>> byEquipType;
/**
* Constructs a new AbstractProfProvider with the given List of proficiency
* references and Equipment TYPE references.
*
* No reference is maintained to the internal structure of the given Lists,
* so modifications to this AbstractProfProvider are not reflected in the
* given Lists (and vice versa).
*
* @param profs
* The List of proficiency references indicating the primitive
* proficiency objects this AbstractProfProvider will contain.
* @param equipTypes
* The List of Equipment references indicating the TYPEs of
* Equipment objects this AbstractProfProvider will contain.
*/
public AbstractProfProvider(List<CDOMReference<T>> profs,
List<CDOMReference<Equipment>> equipTypes)
{
direct = new TreeSet<>(
ReferenceUtilities.REFERENCE_SORTER);
direct.addAll(profs);
byEquipType = new TreeSet<>(
ReferenceUtilities.REFERENCE_SORTER);
byEquipType.addAll(equipTypes);
}
/**
* Returns true if this AbstractProfProvider provides proficiency for the
* given Equipment; false otherwise.
*
* @param equipment
* The Equipment to be tested to see if this AbstractProfProvider
* provides proficiency for the Equipment
* @return true if this AbstractProfProvider provides proficiency for the
* given Equipment; false otherwise.
*/
@Override
public abstract boolean providesProficiencyFor(Equipment equipment);
/**
* Returns true if this AbstractProfProvider provides the given proficiency.
* This only tests against the direct proficiency list provided during
* construction of the AbstractProfProvider.
*
* @param proficiency
* The proficiency to be tested to see if this
* AbstractProfProvider provides the given proficiency
* @return true if this AbstractProfProvider provides the given proficiency;
* false otherwise.
*/
@Override
public boolean providesProficiency(T proficiency)
{
for (CDOMReference<T> ref : direct)
{
if (ref.contains(proficiency))
{
return true;
}
}
return false;
}
/**
* Returns true if this AbstractProfProvider provides proficiency with the
* given Equipment TYPE. This only tests against the Equipment TYPE
* reference list provided during construction of the AbstractProfProvider.
*
* @param typeString
* The TYPE of Equipment to be tested to see if this
* AbstractProfProvider provides proficiency with the given
* Equipment TYPE
* @return true if this AbstractProfProvider provides proficiency with the
* given Equipment TYPE.
*/
@Override
@SuppressWarnings("PMD.AvoidBranchingStatementAsLastInLoop")
public boolean providesEquipmentType(String typeString)
{
if (typeString == null || typeString.isEmpty())
{
return false;
}
Set<String> types = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
Collections.addAll(types, typeString.split("\\."));
REF: for (CDOMReference<Equipment> ref : byEquipType)
{
StringTokenizer tok = new StringTokenizer(ref.getLSTformat(false)
.substring(5), ".");
while (tok.hasMoreTokens())
{
if (!types.contains(tok.nextToken()))
{
continue REF;
}
}
return true;
}
return false;
}
/**
* Returns a String indicating the type of proficiency granted by this
* AbstractProfProvider.
*
* @return A String indicating the type of proficiency granted by this
* AbstractProfProvider
*/
protected abstract String getSubType();
/**
* Returns the LST format for this AbstractProfProvider. Provided primarily
* to allow the Token/Loader system to properly unparse the
* AbstractProfProvider.
*
* @return The LST format of this AbstractProfProvider
*/
@Override
public String getLstFormat()
{
StringBuilder sb = new StringBuilder();
boolean typeEmpty = byEquipType.isEmpty();
if (!direct.isEmpty())
{
sb.append(ReferenceUtilities.joinLstFormat(direct, Constants.PIPE));
if (!typeEmpty)
{
sb.append(Constants.PIPE);
}
}
if (!typeEmpty)
{
boolean needPipe = false;
String subType = getSubType();
String dot = Constants.DOT;
for (CDOMReference<Equipment> ref : byEquipType)
{
if (needPipe)
{
sb.append(Constants.PIPE);
}
needPipe = true;
String lstFormat = ref.getLSTformat(false);
if (lstFormat.startsWith("TYPE="))
{
sb.append(subType).append("TYPE=");
StringTokenizer st = new StringTokenizer(lstFormat
.substring(5), dot);
boolean needDot = false;
while (st.hasMoreTokens())
{
String tok = st.nextToken();
if (!tok.equals(subType))
{
if (needDot)
{
sb.append(dot);
}
needDot = true;
sb.append(tok);
}
}
}
}
}
return sb.toString();
}
/**
* Returns true if the given object is a AbstractProfProvider with identical
* underlying proficiencies, Equipment TYPEs and Prerequisites.
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
if (obj instanceof AbstractProfProvider)
{
AbstractProfProvider<?> other = (AbstractProfProvider<?>) obj;
if (!other.getSubType().equals(getSubType()))
{
return false;
}
if (direct == null)
{
if (other.direct != null)
{
return false;
}
}
else
{
if (!direct.equals(other.direct))
{
return false;
}
}
if (byEquipType == null)
{
if (other.byEquipType != null)
{
return false;
}
}
else
{
if (!byEquipType.equals(other.byEquipType))
{
return false;
}
}
return this.equalsPrereqObject(other);
}
return false;
}
/**
* Returns a consistent-with-equals hashCode for this AbstractProfProvider
*
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
return (direct == null ? 0 : direct.hashCode() * 29)
+ (byEquipType == null ? 0 : byEquipType.hashCode());
}
}