// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.search;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import org.infinity.datatype.Flag;
import org.infinity.datatype.IsNumeric;
import org.infinity.datatype.IsTextual;
import org.infinity.datatype.IwdRef;
import org.infinity.datatype.ResourceRef;
import org.infinity.datatype.Unknown;
import org.infinity.resource.AbstractStruct;
import org.infinity.resource.ResourceFactory;
import org.infinity.resource.StructEntry;
import org.infinity.util.Pair;
import org.infinity.util.io.StreamUtils;
/**
* Stores a list of search options specified in SearchResource (Extended search) for use in the
* resource-specific search methods.
*/
public class SearchOptions
{
// List of option keys for each supported resource type
// Format 1: [Type1.[Type2.[...]]]]Name.Index
// Exception 1: Names starting with an underscore (_) are fetched by offset, index specifies the (relative) offset value
// Exception 2: Names starting with a plus sign (+) are symbolic names with special meanings
// REMEMBER: Keep list of Strings in sync with options provided by SearchResource
// REMEMBER: String values must correspond to the respective resource attribute names
public static final String ARE = "ARE.0"; // marks structure
public static final String ARE_AreaScript = "ARE.Area script.0";
public static final String ARE_AreaType = "ARE.Area type.0";
public static final String ARE_Location = "ARE.Location.0";
public static final String ARE_Actor = "ARE.Actor %1$d.0"; // marks substructure
public static final String ARE_Actor_Character = "ARE.Actor.Character.0";
public static final String ARE_Animation = "ARE.Animation %1$d.0"; // marks substructure
public static final String ARE_Animation_Animation = "ARE.Animation.Animation.0";
public static final String ARE_Container = "ARE.Container %1$d.0"; // marks substructure
public static final String ARE_Container_Item = "ARE.Container.Item %1$d.0"; // marks substructure
public static final String ARE_Container_Item_Item = "ARE.Container.Item.Item.0";
public static final String ARE_Custom1 = "ARE.+Custom.0";
public static final String ARE_Custom2 = "ARE.+Custom.1";
public static final String ARE_Custom3 = "ARE.+Custom.2";
public static final String ARE_Custom4 = "ARE.+Custom.3";
public static final String CRE = "CRE.0"; // marks structure
public static final String CRE_Name = "CRE.Name.0";
public static final String CRE_ScriptName = "CRE.Script name.0";
public static final String CRE_Script1 = "CRE.+Script.0"; // special: matches all available scripts
public static final String CRE_Script2 = "CRE.+Script.1"; // special: matches all available scripts
public static final String CRE_Script3 = "CRE.+Script.2"; // special: matches all available scripts
public static final String CRE_Flags = "CRE.Flags.0";
public static final String CRE_Level1 = "CRE.Level first class.0";
public static final String CRE_Level2 = "CRE.Level second class.0";
public static final String CRE_Level3 = "CRE.Level third class.0";
public static final String CRE_IWD2LevelTotal = "CRE.Total level.0";
public static final String CRE_IWD2LevelBarbarian = "CRE.Barbarian level.0";
public static final String CRE_IWD2LevelBard = "CRE.Bard level.0";
public static final String CRE_IWD2LevelCleric = "CRE.Cleric level.0";
public static final String CRE_IWD2LevelDruid = "CRE.Druid level.0";
public static final String CRE_IWD2LevelFighter = "CRE.Fighter level.0";
public static final String CRE_IWD2LevelMonk = "CRE.Monk level.0";
public static final String CRE_IWD2LevelPaladin = "CRE.Paladin level.0";
public static final String CRE_IWD2LevelRanger = "CRE.Ranger level.0";
public static final String CRE_IWD2LevelRogue = "CRE.Rogue level.0";
public static final String CRE_IWD2LevelSorcerer = "CRE.Sorcerer level.0";
public static final String CRE_IWD2LevelWizard = "CRE.Wizard level.0";
public static final String CRE_General = "CRE.General.0";
public static final String CRE_Class = "CRE.Class.0";
public static final String CRE_Specifics = "CRE.Specifics.0";
public static final String CRE_Alignment = "CRE.Alignment.0";
public static final String CRE_Gender = "CRE.Gender.0";
public static final String CRE_Sex = "CRE.Sex.0"; // IWD2 only
public static final String CRE_Race = "CRE.Race.0";
public static final String CRE_Allegiance = "CRE.Allegiance.0";
public static final String CRE_Kit = "CRE.Kit.0";
public static final String CRE_Animation = "CRE.Animation.0";
public static final String CRE_Feats1 = "CRE.Feats (1/3).0";
public static final String CRE_Feats2 = "CRE.Feats (2/3).0";
public static final String CRE_Feats3 = "CRE.Feats (3/3).0";
public static final String CRE_Attributes = "CRE.Attributes.0";
public static final String CRE_Effect = "CRE.Effect %1$d.0"; // marks substructure
public static final String CRE_Effect_Type1 = "CRE.Effect.Type.0";
public static final String CRE_Effect_Type2 = "CRE.Effect.Type.1";
public static final String CRE_Effect_Type3 = "CRE.Effect.Type.2";
public static final String CRE_Effect_Type4 = "CRE.Effect.Type.3";
public static final String CRE_Item = "CRE.Item %1$d.0"; // marks substructure
public static final String CRE_Item_Item1 = "CRE.Item.Item.0";
public static final String CRE_Item_Item2 = "CRE.Item.Item.1";
public static final String CRE_Item_Item3 = "CRE.Item.Item.2";
public static final String CRE_Item_Item4 = "CRE.Item.Item.3";
public static final String CRE_Spell = "CRE.Known spell %1$d.0"; // marks substructure
public static final String CRE_Spell_Spell1 = "CRE.Known spell.Spell.0";
public static final String CRE_Spell_Spell2 = "CRE.Known spell.Spell.1";
public static final String CRE_Spell_Spell3 = "CRE.Known spell.Spell.2";
public static final String CRE_Spell_Spell4 = "CRE.Known spell.Spell.3";
public static final String CRE_IWD2SpellBard = "CRE.Bard spells %1$d.0"; // marks substructure
public static final String CRE_IWD2SpellBard_Spell = "CRE.Bard spells.Spell.0"; // marks substructure
public static final String CRE_IWD2SpellBard_Spell_ResRef = "CRE.Bard spells.Spell.ResRef.0";
public static final String CRE_IWD2SpellCleric = "CRE.Cleric spells %1$d.0"; // marks substructure
public static final String CRE_IWD2SpellCleric_Spell = "CRE.Cleric spells.Spell.0"; // marks substructure
public static final String CRE_IWD2SpellCleric_Spell_ResRef = "CRE.Cleric spells.Spell.ResRef.0";
public static final String CRE_IWD2SpellDruid = "CRE.Druid spells %1$d.0"; // marks substructure
public static final String CRE_IWD2SpellDruid_Spell = "CRE.Druid spells.Spell.0"; // marks substructure
public static final String CRE_IWD2SpellDruid_Spell_ResRef = "CRE.Druid spells.Spell.ResRef.0";
public static final String CRE_IWD2SpellPaladin = "CRE.Paladin spells %1$d.0"; // marks substructure
public static final String CRE_IWD2SpellPaladin_Spell = "CRE.Paladin spells.Spell.0"; // marks substructure
public static final String CRE_IWD2SpellPaladin_Spell_ResRef = "CRE.Paladin spells.Spell.ResRef.0";
public static final String CRE_IWD2SpellRanger = "CRE.Ranger spells %1$d.0"; // marks substructure
public static final String CRE_IWD2SpellRanger_Spell = "CRE.Ranger spells.Spell.0"; // marks substructure
public static final String CRE_IWD2SpellRanger_Spell_ResRef = "CRE.Ranger spells.Spell.ResRef.0";
public static final String CRE_IWD2SpellSorcerer = "CRE.Sorcerer spells %1$d.0"; // marks substructure
public static final String CRE_IWD2SpellSorcerer_Spell = "CRE.Sorcerer spells.Spell.0"; // marks substructure
public static final String CRE_IWD2SpellSorcerer_Spell_ResRef = "CRE.Sorcerer spells.Spell.ResRef.0";
public static final String CRE_IWD2SpellWizard = "CRE.Wizard spells %1$d.0"; // marks substructure
public static final String CRE_IWD2SpellWizard_Spell = "CRE.Wizard spells.Spell.0"; // marks substructure
public static final String CRE_IWD2SpellWizard_Spell_ResRef = "CRE.Wizard spells.Spell.ResRef.0";
public static final String CRE_IWD2SpellDomain = "CRE.Domain spells %1$d.0"; // marks substructure
public static final String CRE_IWD2SpellDomain_Spell = "CRE.Domain spells.Spell.0"; // marks substructure
public static final String CRE_IWD2SpellDomain_Spell_ResRef = "CRE.Domain spells.Spell.ResRef.0";
public static final String CRE_Custom1 = "CRE.+Custom.0";
public static final String CRE_Custom2 = "CRE.+Custom.1";
public static final String CRE_Custom3 = "CRE.+Custom.2";
public static final String CRE_Custom4 = "CRE.+Custom.3";
public static final String EFF = "EFF.0"; // marks structure
public static final String EFF_Effect = "EFF.Type.0";
public static final String EFF_Param1 = "EFF._Param 1.28"; // special: fetched by offset
public static final String EFF_Param2 = "EFF._Param 2.32"; // special: fetched by offset
public static final String EFF_TimingMode = "EFF.Timing Mode.0";
public static final String EFF_Duration = "EFF.Duration.0";
public static final String EFF_Resource1 = "EFF._Resource 1.48"; // special: fetched by offset
public static final String EFF_Resource2 = "EFF.Resource 2.0";
public static final String EFF_Resource3 = "EFF.Resource 3.0";
public static final String EFF_SaveType = "EFF.Save type.0";
public static final String EFF_Custom1 = "EFF.+Custom.0";
public static final String EFF_Custom2 = "EFF.+Custom.1";
public static final String EFF_Custom3 = "EFF.+Custom.2";
public static final String EFF_Custom4 = "EFF.+Custom.3";
public static final String ITM = "ITM.0"; // marks structure
public static final String ITM_Name = "ITM.Identified name.0";
public static final String ITM_Flags = "ITM.Flags.0";
public static final String ITM_Category = "ITM.Category.0";
public static final String ITM_Unusable = "ITM.Unusable by.0";
public static final String ITM_KitsUnusable1 = "ITM.Unusable by (1/4).0";
public static final String ITM_KitsUnusable2 = "ITM.Unusable by (2/4).0";
public static final String ITM_KitsUnusable3 = "ITM.Unusable by (3/4).0";
public static final String ITM_KitsUnusable4 = "ITM.Unusable by (4/4).0";
public static final String ITM_Appearance = "ITM.Equipped appearance.0";
public static final String ITM_MinLevel = "ITM.Minimum level.0";
public static final String ITM_MinSTR = "ITM.Minimum strength.0";
public static final String ITM_MinSTRExtra = "ITM.Minimum strength bonus.0";
public static final String ITM_MinCON = "ITM.Minimum constitution.0";
public static final String ITM_MinDEX = "ITM.Minimum dexterity.0";
public static final String ITM_MinINT = "ITM.Minimum intelligence.0";
public static final String ITM_MinWIS = "ITM.Minimum wisdom.0";
public static final String ITM_MinCHA = "ITM.Minimum charisma.0";
public static final String ITM_Price = "ITM.Price.0";
public static final String ITM_Enchantment = "ITM.Enchantment.0";
public static final String ITM_Ability = "ITM.Item Ability %1$d.0"; // marks substructure
public static final String ITM_Ability_MatchSingle = "ITM.Item Ability.MatchSingle.0"; // special: indicates whether to limit matches to a single ability
public static final String ITM_Ability_Type = "ITM.Item Ability.Type.0";
public static final String ITM_Ability_Target = "ITM.Item Ability.Target.0";
public static final String ITM_Ability_Launcher = "ITM.Item Ability.Launcher required.0";
public static final String ITM_Ability_DamageType = "ITM.Item Ability.Damage type.0";
public static final String ITM_Ability_Projectile = "ITM.Item Ability.Projectile.0";
public static final String ITM_Ability_Range = "ITM.Item Ability.Range (feet).0";
public static final String ITM_Ability_Speed = "ITM.Item Ability.Speed.0";
public static final String ITM_Ability_DiceCount = "ITM.Item Ability.# dice thrown.0";
public static final String ITM_Ability_DiceSize = "ITM.Item Ability.Dice size.0";
public static final String ITM_Ability_Charges = "ITM.Item Ability.# charges.0";
public static final String ITM_Ability_Effect = "ITM.Item Ability.Effect %1$d.0"; // marks substructure
public static final String ITM_Ability_Effect_Type1 = "ITM.Item Ability.Effect.Type.0";
public static final String ITM_Ability_Effect_Type2 = "ITM.Item Ability.Effect.Type.1";
public static final String ITM_Ability_Effect_Type3 = "ITM.Item Ability.Effect.Type.2";
public static final String ITM_Ability_Flags = "ITM.Item Ability.Flags.0";
public static final String ITM_Effect = "ITM.Effect %1$d.0"; // marks substructure
public static final String ITM_Effect_Type1 = "ITM.Effect.Type.0";
public static final String ITM_Effect_Type2 = "ITM.Effect.Type.1";
public static final String ITM_Effect_Type3 = "ITM.Effect.Type.2";
public static final String ITM_Custom1 = "ITM.+Custom.0";
public static final String ITM_Custom2 = "ITM.+Custom.1";
public static final String ITM_Custom3 = "ITM.+Custom.2";
public static final String ITM_Custom4 = "ITM.+Custom.3";
public static final String PRO = "PRO.0"; // marks structure
public static final String PRO_Animation = "PRO.Projectile animation.0";
public static final String PRO_Type = "PRO.Projectile type.0";
public static final String PRO_Speed = "PRO.Speed.0";
public static final String PRO_Behavior = "PRO.Behavior.0";
public static final String PRO_Flags = "PRO.Flags.0";
public static final String PRO_AreaFlags = "PRO.Area flags.0";
public static final String PRO_TrapSize = "PRO.Trap size.0";
public static final String PRO_ExplosionSize = "PRO.Explosion size.0";
public static final String PRO_ExplosionEffect = "PRO.Explosion effect.0";
public static final String PRO_SingleTarget = "PRO.Projectile info.0"; // marks substructure
public static final String PRO_AreaOfEffect = "PRO.Area effect info.0"; // marks substructure
public static final String PRO_Custom1 = "PRO.+Custom.0";
public static final String PRO_Custom2 = "PRO.+Custom.1";
public static final String PRO_Custom3 = "PRO.+Custom.2";
public static final String PRO_Custom4 = "PRO.+Custom.3";
public static final String SPL = "SPL.0"; // marks structure
public static final String SPL_Name = "SPL.Spell name.0";
public static final String SPL_Flags = "SPL.Flags.0";
public static final String SPL_SpellType = "SPL.Spell type.0";
public static final String SPL_Exclusion = "SPL.Exclusion flags.0";
public static final String SPL_CastingAnimation = "SPL.Casting animation.0";
public static final String SPL_PrimaryType = "SPL.Primary type (school).0";
public static final String SPL_SecondaryType = "SPL.Secondary type.0";
public static final String SPL_Level = "SPL.Spell level.0";
public static final String SPL_Ability = "SPL.Spell ability %1$d.0"; // marks substructure
public static final String SPL_Ability_MatchSingle = "SPL.Spell ability.MatchSingle.0"; // special: indicates whether to limit matches to a single ability
public static final String SPL_Ability_Type = "SPL.Spell ability.Type.0";
public static final String SPL_Ability_Location = "SPL.Spell ability.Ability location.0";
public static final String SPL_Ability_Target = "SPL.Spell ability.Target.0";
public static final String SPL_Ability_Range = "SPL.Spell ability.Range (feet).0";
public static final String SPL_Ability_Level = "SPL.Spell ability.Minimum level.0";
public static final String SPL_Ability_Speed = "SPL.Spell ability.Casting speed.0";
public static final String SPL_Ability_Projectile = "SPL.Spell ability.Projectile.0";
public static final String SPL_Ability_Effect = "SPL.Spell ability.Effect %1$d.0"; // marks substructure
public static final String SPL_Ability_Effect_Type1 = "SPL.Spell ability.Effect.Type.0";
public static final String SPL_Ability_Effect_Type2 = "SPL.Spell ability.Effect.Type.1";
public static final String SPL_Ability_Effect_Type3 = "SPL.Spell ability.Effect.Type.2";
public static final String SPL_Effect = "SPL.Effect %1$d.0"; // marks substructure
public static final String SPL_Effect_Type1 = "SPL.Effect.Type.0";
public static final String SPL_Effect_Type2 = "SPL.Effect.Type.1";
public static final String SPL_Effect_Type3 = "SPL.Effect.Type.2";
public static final String SPL_Custom1 = "SPL.+Custom.0";
public static final String SPL_Custom2 = "SPL.+Custom.1";
public static final String SPL_Custom3 = "SPL.+Custom.2";
public static final String SPL_Custom4 = "SPL.+Custom.3";
public static final String STO = "STO.0"; // marks structure
public static final String STO_Name = "STO.Name.0";
public static final String STO_Type = "STO.Type.0";
public static final String STO_Flags = "STO.Flags.0";
public static final String STO_SellMarkup = "STO.Sell markup.0";
public static final String STO_BuyMarkup = "STO.Buy markup.0";
public static final String STO_Stealing = "STO.Stealing difficulty.0";
public static final String STO_Capacity = "STO.Storage capacity.0";
public static final String STO_Depreciation = "STO.Depreciation rate.0";
public static final String STO_RoomsAvailable = "STO.Available rooms.0";
public static final String STO_Purchased = "STO.Store purchases %1$d.0"; // marks addremovable field
public static final String STO_Purchased1 = "STO.Store purchases.0";
public static final String STO_Purchased2 = "STO.Store purchases.1";
public static final String STO_Purchased3 = "STO.Store purchases.2";
public static final String STO_Purchased4 = "STO.Store purchases.3";
public static final String STO_Purchased5 = "STO.Store purchases.4";
public static final String STO_Item = "STO.Item for sale %1$d.0"; // marks substructure
public static final String STO_Item_Item1 = "STO.Item for sale.Item.0";
public static final String STO_Item_Item2 = "STO.Item for sale.Item.1";
public static final String STO_Item_Item3 = "STO.Item for sale.Item.2";
public static final String STO_Item_Item4 = "STO.Item for sale.Item.3";
public static final String STO_Item_Item5 = "STO.Item for sale.Item.4";
public static final String STO_Custom1 = "STO.+Custom.0";
public static final String STO_Custom2 = "STO.+Custom.1";
public static final String STO_Custom3 = "STO.+Custom.2";
public static final String STO_Custom4 = "STO.+Custom.3";
public static final String VVC = "VVC.0"; // marks structure
public static final String VVC_Animation = "VVC.Animation.0";
public static final String VVC_Flags = "VVC.Drawing.0";
public static final String VVC_ColorAdjustment = "VVC.Color adjustment.0";
public static final String VVC_Sequencing = "VVC.Sequencing.0";
public static final String VVC_Orientation = "VVC.Travel orientation.0";
public static final String VVC_Custom1 = "VVC.+Custom.0";
public static final String VVC_Custom2 = "VVC.+Custom.1";
public static final String VVC_Custom3 = "VVC.+Custom.2";
public static final String VVC_Custom4 = "VVC.+Custom.3";
private final HashMap<String, Object> mapOptions = new HashMap<String, Object>();
private final String resourceType;
/**
* Extracts the resource type from the specified option key.
* @param key The option key to extract the resource type from.
* @return The resource type associated with the option key.
*/
public static String getResourceType(String key)
{
if (key != null) {
String[] segments = key.split("\\.");
if (segments != null) {
if (segments.length > 2) {
return segments[segments.length - 3];
}
}
}
return "";
}
/**
* Extracts the resource name from the specified option key.
* @param key The option key to extract the resource name from.
* @return The resource name associated with the option key.
*/
public static String getResourceName(String key)
{
if (key != null) {
String[] segments = key.split("\\.");
if (segments != null) {
if (segments.length > 1) {
return segments[segments.length - 2];
}
}
}
return "";
}
/**
* Extracts the resource index (or offset) from the specified option key.
* @param key The option key to extract the resource index/offset from.
* @return The resource index/offset associated with the option key.
*/
public static int getResourceIndex(String key)
{
if (key != null) {
String[] segments = key.split("\\.");
if (segments != null) {
if (segments.length > 0) {
try {
return Integer.parseInt(segments[segments.length - 1]);
} catch (NumberFormatException e) {
}
}
}
}
return 0;
}
/**
* Returns the nested level of the option key.
* @param name The option key to extract the resource index from.
* @return The nested level (0=no resource type defined, 1=first level resource type, ...)
*/
public static int getResourceNameLevel(String key)
{
if (key != null) {
String[] segments = key.split("\\.");
if (segments != null) {
if (segments.length > 1) {
return segments.length - 2;
}
}
}
return 0;
}
/**
* Returns whether the specified option key defines a resource field by offset rather than by name.
* @param key The option key.
* @return {@code true} if the specified option key defines a resource field by offset.
*/
public static boolean isResourceByOffset(String key)
{
if (key != null) {
String[] segments = key.split("\\.");
if (segments != null) {
if (segments.length > 1) {
return segments[segments.length - 2].startsWith("_");
}
}
}
return false;
}
/**
* Initializes a new empty SearchOptions container.
* @param resourceType The resource type to identify the list of option keys.
*/
public SearchOptions(String resourceType)
{
this.resourceType = resourceType;
}
/**
* Returns true if this instance doesn't contain any option entries.
* @return true if this instance doesn't contain any option entries.
*/
public boolean isEmpty()
{
return mapOptions.isEmpty();
}
/**
* Returns the number of option entries.
* @return Number of option entries.
*/
public int size()
{
return mapOptions.size();
}
/**
* Returns the resource type the option entries are referring to.
* @return The resource type as file extension string.
*/
public String getResourceType()
{
return resourceType;
}
/**
* Returns the option of the specified name as an option-specific Object type.
* @param name The name of the option.
* @return The option object associated with the specified name, or {@code null} if not found.
*/
public Object getOption(String name)
{
if (name != null) {
return mapOptions.get(name);
}
return null;
}
/**
* Adds the specified name/value pair into the list. If {@code value} is {@code null}, an
* existing entry of the specified name will be removed.
* @param name The name of the option to add.
* @param value The value of the option to add. Special: If {@code value} is {@code null},
* an existing option of the same name will be removed.
* @return The previous value of the option, or {@code null} if no previous option of the
* specified name exists.
*/
public Object setOption(String name, Object value)
{
if (name != null) {
if (value != null) {
return mapOptions.put(name, value);
} else {
return mapOptions.remove(name);
}
}
return null;
}
/**
* Returns a list of available option keys in the options list.
* @return A list of available option keys.
*/
public String[] getOptionKeys()
{
String[] retVal = new String[mapOptions.keySet().size()];
Iterator<String> iter = mapOptions.keySet().iterator();
int idx = 0;
while (iter.hasNext()) {
retVal[idx++] = iter.next();
}
return retVal;
}
//-------------------------- INNER CLASSES --------------------------
public static final class Utils
{
// Returns whether ref and value are equal, optionally taking case-sensitivity into account.
public static boolean matchResourceRef(StructEntry ref, Object value, boolean caseSensitive)
{
if (ref != null && value != null ) {
String s1, s2;
if (ref instanceof ResourceRef && value instanceof String) {
s1 = (String)value;
s2 = ((ResourceRef)ref).getResourceName();
} else if (ref instanceof IwdRef) {
if (value instanceof Integer) {
s1 = ((IwdRef)ref).getValueRef((Integer)value);
} else if (value instanceof Long) {
s1 = ((IwdRef)ref).getValueRef((Long)value);
} else if (value instanceof String) {
s1 = (String)value;
} else {
return false;
}
s2 = ((IwdRef)ref).getValueRef();
} else {
return false;
}
// special case: "NONE"
if ((s1.isEmpty() || "NONE".equalsIgnoreCase(s1)) &&
(s2.isEmpty() || "NONE".equalsIgnoreCase(s2) || ResourceFactory.getResourceEntry(s2) == null)) {
return true;
}
if (caseSensitive) {
return s1.equals(s2);
} else {
return s1.equalsIgnoreCase(s2);
}
}
return (value == null);
}
// Returns whether entry contains (or equals) value, optionally taking case-sensitivity into account.
public static boolean matchString(StructEntry entry, Object value, boolean exact, boolean caseSensitive)
{
if (entry != null && value != null && value instanceof String) {
// preparing source
String s1 = (String)value;
if (s1.isEmpty()) {
return false;
}
// preparing target
String s2;
if (entry instanceof IsTextual) {
s2 = ((IsTextual)entry).getText();
} else if (entry instanceof Unknown) {
ByteBuffer buf = ((Unknown)entry).getData();
s2 = StreamUtils.readString(buf, 0, buf.limit());
} else {
return false;
}
// comparing strings
if (s2 != null) {
if (exact) {
if (caseSensitive) {
return s1.equals(s2);
} else {
return s1.equalsIgnoreCase(s2);
}
} else {
if (caseSensitive) {
return s2.contains(s1);
} else {
return s2.toLowerCase(Locale.ENGLISH).contains(s1.toLowerCase(Locale.ENGLISH));
}
}
}
}
return (value == null);
}
// Returns whether all bits match (exact=true) or only the set bits match (exact=false)
public static boolean matchFlags(StructEntry flag, Object value)
{
if (flag != null && flag instanceof Flag && value != null) {
boolean retVal = true;
int v;
boolean isExact;
if (value instanceof Pair<?> && ((Pair<?>)value).getFirst() instanceof Integer &&
((Pair<?>)value).getSecond() instanceof Boolean) {
v = (Integer)((Pair<?>)value).getFirst();
isExact = (Boolean)((Pair<?>)value).getSecond();
} else if (value instanceof Integer) {
v = (Integer)value;
isExact = false;
} else {
return false;
}
for (int mask = 1, bit = 0; bit < (flag.getSize() << 3); bit++, mask <<= 1) {
if (isExact) {
if (((v & mask) != 0) != ((Flag)flag).isFlagSet(bit)) {
retVal = false;
break;
}
} else {
if (((v & mask) != 0) && !((Flag)flag).isFlagSet(bit)) {
retVal = false;
break;
}
}
}
return retVal;
}
return (value == null);
}
// Returns whether number and value are equal
public static boolean matchNumber(StructEntry number, Object value)
{
if (number != null && value != null) {
// preparations
int n1, n2, n3;
if (value instanceof Integer) {
n1 = n2 = (Integer)value;
} else if (value instanceof Pair<?> &&
((Pair<?>)value).getFirst() instanceof Integer &&
((Pair<?>)value).getSecond() instanceof Integer) {
n1 = (Integer)((Pair<?>)value).getFirst();
n2 = (Integer)((Pair<?>)value).getSecond();
if (n1 > n2) { int tmp = n1; n1 = n2; n2 = tmp; }
} else {
return false;
}
// supported number-related datatypes
if (number instanceof IsNumeric) {
n3 = ((IsNumeric)number).getValue();
} else {
return false;
}
return (n3 >= n1 && n3 <= n2);
}
return (value == null);
}
public static boolean matchCustomFilter(AbstractStruct struct, Object match)
{
if (struct != null && match != null && match instanceof Pair<?> &&
((Pair<?>)match).getFirst() instanceof String) {
String fieldName = (String)((Pair<?>)match).getFirst();
Object value = ((Pair<?>)match).getSecond();
if (!fieldName.isEmpty() && value != null) {
boolean bRet = false;
List<StructEntry> structList = struct.getFlatList();
if (structList != null && !structList.isEmpty()) {
for (int i = 0; i < structList.size(); i++) {
StructEntry entry = structList.get(i);
if (entry != null) {
String name = entry.getName();
if (name.toUpperCase(Locale.ENGLISH).contains(fieldName.toUpperCase(Locale.ENGLISH))) {
// field name matches
if (value instanceof String) {
bRet |= matchResourceRef(entry, value, false) || matchString(entry, value, false, false);
} else if (value instanceof Pair<?>) {
if (((Pair<?>)value).getFirst() instanceof Integer) {
if (((Pair<?>)value).getSecond() instanceof Integer) {
bRet |= matchNumber(entry, value);
} else if (((Pair<?>)value).getSecond() instanceof Boolean) {
bRet |= matchFlags(entry, value);
}
}
}
}
}
}
return bRet;
} else {
return false;
}
}
}
return (match == null);
}
}
}