Merge pull request #223 from yueh/feature-129

Optimizes the search in interface terminals
This commit is contained in:
Chris
2014-10-01 20:26:21 -07:00
@@ -3,15 +3,21 @@ package appeng.client.gui.implementations;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagCompound;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import appeng.api.storage.data.IAEItemStack;
import appeng.client.gui.AEBaseGui; import appeng.client.gui.AEBaseGui;
import appeng.client.gui.widgets.GuiScrollbar; import appeng.client.gui.widgets.GuiScrollbar;
import appeng.client.gui.widgets.MEGuiTextField; import appeng.client.gui.widgets.MEGuiTextField;
@@ -19,7 +25,9 @@ import appeng.client.me.ClientDCInternalInv;
import appeng.client.me.SlotDisconnected; import appeng.client.me.SlotDisconnected;
import appeng.container.implementations.ContainerInterfaceTerminal; import appeng.container.implementations.ContainerInterfaceTerminal;
import appeng.core.localization.GuiText; import appeng.core.localization.GuiText;
import appeng.helpers.PatternHelper;
import appeng.parts.reporting.PartMonitor; import appeng.parts.reporting.PartMonitor;
import appeng.util.Platform;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
@@ -37,6 +45,8 @@ public class GuiInterfaceTerminal extends AEBaseGui
private final ArrayList<Object> lines = new ArrayList<Object>(); private final ArrayList<Object> lines = new ArrayList<Object>();
private final EntityPlayer player; private final EntityPlayer player;
private final Map<String, Set<Object>> cachedSearches = new WeakHashMap<String, Set<Object>>();
private boolean refreshList = false; private boolean refreshList = false;
private MEGuiTextField searchField; private MEGuiTextField searchField;
@@ -116,8 +126,8 @@ public class GuiInterfaceTerminal extends AEBaseGui
ClientDCInternalInv inv = (ClientDCInternalInv) lineObj; ClientDCInternalInv inv = (ClientDCInternalInv) lineObj;
GL11.glColor4f( 1, 1, 1, 1 ); GL11.glColor4f( 1, 1, 1, 1 );
for (int z = 0; z < inv.inv.getSizeInventory(); z++) int width = inv.inv.getSizeInventory() * 18;
this.drawTexturedModalRect( offsetX + z * 18 + 7, offsetY + offset, 7, 139, 18, 18 ); this.drawTexturedModalRect( offsetX + 7, offsetY + offset, 7, 139, width, 18 );
} }
offset += 18; offset += 18;
} }
@@ -204,12 +214,14 @@ public class GuiInterfaceTerminal extends AEBaseGui
if ( refreshList ) if ( refreshList )
{ {
refreshList = false; refreshList = false;
// invalid caches on refresh
cachedSearches.clear();
refreshList(); refreshList();
} }
} }
/** /**
* rebuilds the list of interfaces. * Rebuilds the list of interfaces.
* *
* Respects a search term if present (ignores case) and adding only matching patterns. * Respects a search term if present (ignores case) and adding only matching patterns.
*/ */
@@ -219,11 +231,19 @@ public class GuiInterfaceTerminal extends AEBaseGui
final String searchFilterLowerCase = searchField.getText().toLowerCase(); final String searchFilterLowerCase = searchField.getText().toLowerCase();
final Set<Object> cachedSearch = this.getCacheForSearchTerm( searchFilterLowerCase );
final boolean rebuild = cachedSearch.isEmpty();
for (ClientDCInternalInv entry : byId.values()) for (ClientDCInternalInv entry : byId.values())
{ {
// ignore inventory if not doing a full rebuild or cache already marks it as miss.
if ( !rebuild && !cachedSearch.contains( entry ) )
continue;
// Shortcut to skip any filter if search term is ""/empty // Shortcut to skip any filter if search term is ""/empty
boolean found = searchFilterLowerCase.isEmpty(); boolean found = searchFilterLowerCase.isEmpty();
// Search if the current inventory holds a pattern containing the search term. // Search if the current inventory holds a pattern containing the search term.
if ( !found && !searchFilterLowerCase.equals( "" ) ) if ( !found && !searchFilterLowerCase.equals( "" ) )
{ {
@@ -231,11 +251,15 @@ public class GuiInterfaceTerminal extends AEBaseGui
{ {
if ( itemStack != null ) if ( itemStack != null )
{ {
String tooltipLowerCase = String.valueOf( itemStack.getTooltip( player, false ) ).toLowerCase(); final PatternHelper ph = new PatternHelper( itemStack, player.worldObj );
if ( tooltipLowerCase.contains( searchFilterLowerCase ) ) final IAEItemStack[] output = ph.getCondensedOutputs();
for (IAEItemStack iaeItemStack : output)
{ {
found = true; if ( Platform.getItemDisplayName( iaeItemStack ).toLowerCase().contains( searchFilterLowerCase ) )
break; {
found = true;
break;
}
} }
} }
} }
@@ -243,7 +267,14 @@ public class GuiInterfaceTerminal extends AEBaseGui
// if found, filter skipped or machine name matching the search term, add it // if found, filter skipped or machine name matching the search term, add it
if ( found || entry.getName().toLowerCase().contains( searchFilterLowerCase ) ) if ( found || entry.getName().toLowerCase().contains( searchFilterLowerCase ) )
{
byName.put( entry.getName(), entry ); byName.put( entry.getName(), entry );
cachedSearch.add( entry );
}
else
{
cachedSearch.remove( entry );
}
} }
names.clear(); names.clear();
@@ -268,6 +299,40 @@ public class GuiInterfaceTerminal extends AEBaseGui
myScrollBar.setRange( 0, lines.size() - LINES_ON_PAGE, 2 ); myScrollBar.setRange( 0, lines.size() - LINES_ON_PAGE, 2 );
} }
/**
* Tries to retrieve a cache for a with search term as keyword.
*
* If this cache should be empty, it will populate it with an earlier cache if available or at least the cache for
* the empty string.
*
* @param searchTerm
* the corresponding search
* @return a Set matching a superset of the search term
*/
private Set<Object> getCacheForSearchTerm(String searchTerm)
{
if ( !cachedSearches.containsKey( searchTerm ) )
{
cachedSearches.put( searchTerm, new HashSet<Object>() );
}
Set<Object> cache = cachedSearches.get( searchTerm );
if ( cache.isEmpty() )
{
if ( searchTerm.length() > 1 && cachedSearches.containsKey( searchTerm.substring( 0, searchTerm.length() - 1 ) ) )
{
cache.addAll( cachedSearches.get( searchTerm.substring( 0, searchTerm.length() - 1 ) ) );
}
else
{
cache.addAll( cachedSearches.get( "" ) );
}
}
return cache;
}
/** /**
* The max amount of unique names and each inv row. Not affected by the filtering. * The max amount of unique names and each inv row. Not affected by the filtering.
* *