// 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.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.ProgressMonitor;
import org.infinity.datatype.TextString;
import org.infinity.gui.Center;
import org.infinity.gui.ChildFrame;
import org.infinity.gui.ViewerUtil;
import org.infinity.icon.Icons;
import org.infinity.resource.Resource;
import org.infinity.resource.ResourceFactory;
import org.infinity.resource.StructEntry;
import org.infinity.resource.cre.CreResource;
import org.infinity.resource.key.ResourceEntry;
import org.infinity.util.Debugging;
import org.infinity.util.Misc;
abstract class AbstractReferenceSearcher implements Runnable, ActionListener
{
protected static final String[] FILE_TYPES = {"2DA", "ARE", "BCS", "CHR", "CHU", "CRE", "DLG",
"EFF", "GAM", "INI", "ITM", "PRO", "SAV", "SPL",
"STO", "VEF", "VVC", "WED", "WMP"};
private static final String FMT_PROGRESS = "Processing %ss...";
private static HashMap<String, Boolean[]> lastSelection = new HashMap<String, Boolean[]>();
final ResourceEntry targetEntry;
private final ChildFrame selectframe = new ChildFrame("References", true);
private final Component parent;
private final JButton bStart = new JButton("Search", Icons.getIcon(Icons.ICON_FIND_16));
private final JButton bCancel = new JButton("Cancel", Icons.getIcon(Icons.ICON_DELETE_16));
private final JButton bInvert = new JButton("Invert");
private final JButton bClear = new JButton("Clear");
private final JButton bSet = new JButton("Set");
private final JButton bDefault = new JButton("Default");
private final ReferenceHitFrame hitFrame;
private final String[] filetypes;
private final boolean[] preselect;
protected String targetEntryName; // optional alternate name to search for
private JCheckBox[] boxes;
private List<ResourceEntry> files;
private ProgressMonitor progress;
private int progressIndex;
AbstractReferenceSearcher(ResourceEntry targetEntry, String filetypes[], Component parent)
{
this(targetEntry, filetypes, setSelectedFileTypes(targetEntry, filetypes), parent);
}
AbstractReferenceSearcher(ResourceEntry targetEntry, String filetypes[], boolean[] preselect, Component parent)
{
this.targetEntry = targetEntry;
if (getTargetExtension().equalsIgnoreCase("CRE")) {
String resName = targetEntry.getResourceName();
if (resName.lastIndexOf('.') > 0) {
resName = resName.substring(0, resName.lastIndexOf('.'));
}
try {
CreResource cre = new CreResource(targetEntry);
StructEntry nameEntry = cre.getAttribute(CreResource.CRE_SCRIPT_NAME);
if (nameEntry instanceof TextString) {
targetEntryName = ((TextString)nameEntry).toString();
// ignore specific script names
if (targetEntryName.equalsIgnoreCase("None")) {
targetEntryName = null;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
this.filetypes = filetypes;
this.preselect = preselect;
this.parent = parent;
hitFrame = new ReferenceHitFrame(targetEntry, parent);
if (filetypes.length == 1) {
files = new ArrayList<ResourceEntry>();
files.addAll(ResourceFactory.getResources(filetypes[0]));
if (files.size() > 0)
new Thread(this).start();
}
else {
boxes = new JCheckBox[filetypes.length];
bStart.setMnemonic('s');
bCancel.setMnemonic('c');
bInvert.setMnemonic('i');
bDefault.setMnemonic('d');
bStart.addActionListener(this);
bCancel.addActionListener(this);
bInvert.addActionListener(this);
bClear.addActionListener(this);
bSet.addActionListener(this);
bDefault.addActionListener(this);
selectframe.getRootPane().setDefaultButton(bStart);
selectframe.setIconImage(Icons.getIcon(Icons.ICON_FIND_16).getImage());
JPanel boxpanel = new JPanel(new GridLayout(0, 2, 3, 3));
for (int i = 0; i < boxes.length; i++) {
boxes[i] = new JCheckBox(filetypes[i], isPreselected(filetypes, preselect, i));
boxpanel.add(boxes[i]);
}
boxpanel.setBorder(BorderFactory.createEmptyBorder(3, 12, 3, 0));
Boolean[] selection = lastSelection.get(getTargetExtension());
if (selection != null) {
for (int i = 0; i < selection.length; i++) {
boxes[i].setSelected(selection[i]);
}
}
GridBagConstraints gbc = new GridBagConstraints();
JPanel ipanel1 = new JPanel(new GridBagLayout());
gbc = ViewerUtil.setGBC(gbc, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START,
GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0);
ipanel1.add(bClear, gbc);
gbc = ViewerUtil.setGBC(gbc, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START,
GridBagConstraints.HORIZONTAL, new Insets(0, 8, 0, 0), 0, 0);
ipanel1.add(bSet, gbc);
gbc = ViewerUtil.setGBC(gbc, 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START,
GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0);
ipanel1.add(bDefault, gbc);
gbc = ViewerUtil.setGBC(gbc, 1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START,
GridBagConstraints.HORIZONTAL, new Insets(4, 8, 0, 0), 0, 0);
ipanel1.add(bInvert, gbc);
JPanel ipanel = new JPanel(new BorderLayout());
ipanel.add(ipanel1, BorderLayout.CENTER);
JPanel innerpanel = new JPanel(new BorderLayout());
innerpanel.add(boxpanel, BorderLayout.CENTER);
innerpanel.add(ipanel, BorderLayout.SOUTH);
innerpanel.setBorder(BorderFactory.createTitledBorder("Select files to search:"));
JPanel bpanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
bpanel.add(bStart);
bpanel.add(bCancel);
JPanel mainpanel = new JPanel(new BorderLayout());
mainpanel.add(innerpanel, BorderLayout.CENTER);
mainpanel.add(bpanel, BorderLayout.SOUTH);
mainpanel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
JPanel pane = (JPanel)selectframe.getContentPane();
pane.setLayout(new BorderLayout());
pane.add(mainpanel, BorderLayout.CENTER);
selectframe.pack();
Center.center(selectframe, parent.getBounds());
selectframe.setVisible(true);
}
}
// --------------------- Begin Interface ActionListener ---------------------
@Override
public void actionPerformed(ActionEvent event)
{
if (event.getSource() == bStart) {
selectframe.setVisible(false);
files = new ArrayList<ResourceEntry>();
Boolean[] selection = lastSelection.get(getTargetExtension());
if (selection == null) {
selection = new Boolean[filetypes.length];
lastSelection.put(getTargetExtension(), selection);
}
for (int i = 0; i < filetypes.length; i++) {
if (boxes[i].isSelected()) {
files.addAll(ResourceFactory.getResources(filetypes[i]));
}
selection[i] = Boolean.valueOf(boxes[i].isSelected());
}
if (files.size() > 0) {
new Thread(this).start();
}
}
else if (event.getSource() == bCancel) {
selectframe.setVisible(false);
}
else if (event.getSource() == bInvert) {
for (final JCheckBox box: boxes) {
box.setSelected(!box.isSelected());
}
}
else if (event.getSource() == bClear) {
for (final JCheckBox box: boxes) {
box.setSelected(false);
}
}
else if (event.getSource() == bSet) {
for (final JCheckBox box: boxes) {
box.setSelected(true);
}
}
else if (event.getSource() == bDefault) {
if (preselect != null) {
for (int i = 0; i < boxes.length; i++) {
if (preselect != null) {
boxes[i].setSelected((i < preselect.length) ? preselect[i] : false);
} else {
boxes[i].setSelected(true);
}
}
}
}
}
// --------------------- End Interface ActionListener ---------------------
// --------------------- Begin Interface Runnable ---------------------
@Override
public void run()
{
try {
// executing multithreaded search
boolean isCancelled = false;
ThreadPoolExecutor executor = Misc.createThreadPool();
String type = "";
progressIndex = 0;
progress = new ProgressMonitor(parent, "Searching...",
String.format(FMT_PROGRESS, "WWWW"),
0, files.size());
progress.setMillisToDecideToPopup(100);
Debugging.timerReset();
for (int i = 0; i < files.size(); i++) {
ResourceEntry entry = files.get(i);
if (i % 10 == 0) {
String ext = entry.getExtension();
if (ext != null && !type.equalsIgnoreCase(ext)) {
type = ext;
progress.setNote(String.format(FMT_PROGRESS, type));
}
}
Misc.isQueueReady(executor, true, -1);
executor.execute(new Worker(entry));
if (progress.isCanceled()) {
isCancelled = true;
break;
}
}
// enforcing thread termination if process has been cancelled
if (isCancelled) {
executor.shutdownNow();
} else {
executor.shutdown();
}
// waiting for pending threads to terminate
while (!executor.isTerminated()) {
if (!isCancelled && progress.isCanceled()) {
executor.shutdownNow();
isCancelled = true;
}
try { Thread.sleep(1); } catch (InterruptedException e) {}
}
if (isCancelled) {
hitFrame.close();
JOptionPane.showMessageDialog(parent, "Search cancelled", "Info", JOptionPane.INFORMATION_MESSAGE);
} else {
hitFrame.setVisible(true);
}
} finally {
advanceProgress(true);
}
Debugging.timerShow("Search completed", Debugging.TimeFormat.MILLISECONDS);
}
// --------------------- End Interface Runnable ---------------------
synchronized void addHit(ResourceEntry entry, String name, StructEntry ref)
{
hitFrame.addHit(entry, name, ref);
}
abstract void search(ResourceEntry entry, Resource resource);
ResourceEntry getTargetEntry()
{
return targetEntry;
}
String getTargetExtension()
{
return (targetEntry != null) ? targetEntry.getExtension() : "";
}
private boolean isPreselected(String[] filetypes, boolean[] preselect, int index)
{
boolean retVal = true;
if (index >= 0 && filetypes != null && filetypes.length > index) {
if (preselect != null && preselect.length > index && !preselect[index]) {
retVal = false;
}
}
return retVal;
}
protected static boolean[] setSelectedFileTypes(ResourceEntry entry, String[] filetypes)
{
boolean[] retVal = null;
if (entry != null && filetypes != null && filetypes.length > 0) {
retVal = new boolean[filetypes.length];
for (int i = 0; i < retVal.length; i++) retVal[i] = false;
String[] selectedExt = null;
String ext = entry.getExtension();
if ("ARE".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"2DA", "BCS", "DLG", "GAM", "WMP"};
} else if ("BAM".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"2DA", "ARE", "BCS", "CHU", "CRE", "DLG", "EFF",
"GAM", "INI", "ITM", "PRO", "SPL", "VEF", "VVC", "WMP"};
} else if ("BMP".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"2DA", "ARE", "BCS", "CHU", "CRE", "DLG", "EFF", "GAM",
"INI", "ITM", "PRO", "SPL", "VEF", "VVC"};
} else if ("CRE".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"2DA", "ARE", "BCS", "DLG", "EFF", "GAM", "INI", "ITM", "SPL"};
} else if ("DLG".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"2DA", "BCS", "CRE", "DLG", "GAM"};
} else if ("EFF".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"CRE", "EFF", "GAM", "ITM", "SPL"};
} else if ("FNT".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"CHU"};
} else if ("ITM".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"2DA", "ARE", "BCS", "CRE", "DLG", "EFF", "GAM", "ITM",
"SPL", "STO"};
} else if ("MOS".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"2DA", "ARE", "BCS", "CHU", "DLG", "WMP"};
} else if ("MVE".equalsIgnoreCase(ext) || "WBM".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"ARE", "BCS", "CRE", "DLG", "EFF", "GAM", "ITM", "SPL"};
} else if ("PNG".equalsIgnoreCase(ext)) {
// TODO: confirm!
selectedExt = new String[]{"2DA", "ARE", "BCS", "CHU", "CRE", "DLG", "EFF", "GAM",
"INI", "ITM", "PRO", "SPL", "VEF", "VVC"};
} else if ("PRO".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"ARE", "CRE", "EFF", "GAM", "ITM", "SPL"};
} else if ("PVRZ".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"BAM", "MOS", "TIS"};
} else if ("SPL".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"2DA", "ARE", "BCS", "CRE", "DLG", "EFF", "GAM",
"ITM", "SPL"};
} else if ("STO".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"BCS", "DLG"};
} else if ("TIS".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"WED"};
} else if ("VVC".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"ARE", "BCS", "DLG", "EFF", "GAM", "INI", "ITM", "PRO",
"SPL", "VEF", "VVC"};
} else if ("WAV".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"2DA", "ARE", "BCS", "CRE", "DLG", "EFF", "GAM", "INI",
"ITM", "PRO", "SPL", "VEF", "VVC"};
} else if ("WED".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"ARE"};
} else if ("WMP".equalsIgnoreCase(ext)) {
selectedExt = new String[]{"BCS", "DLG"};
}
// defining preselection
if (selectedExt != null && selectedExt.length > 0) {
for (int i = 0; i < retVal.length; i++) {
for (int j = 0; j < selectedExt.length; j++) {
if (filetypes[i] != null && filetypes[i].equalsIgnoreCase(selectedExt[j])) {
retVal[i] = true;
break;
}
}
}
} else {
// select all by default
for (int i = 0; i < retVal.length; i++) {
retVal[i] = true;
}
}
}
return retVal;
}
private synchronized void advanceProgress(boolean finished)
{
if (progress != null) {
if (finished) {
progressIndex = 0;
progress.close();
progress = null;
} else {
progressIndex++;
progress.setProgress(progressIndex);
}
}
}
//-------------------------- INNER CLASSES --------------------------
private class Worker implements Runnable
{
private final ResourceEntry entry;
public Worker(ResourceEntry entry)
{
this.entry = entry;
}
@Override
public void run()
{
if (entry != null) {
Resource resource = ResourceFactory.getResource(entry);
if (resource != null) {
search(entry, resource);
}
}
advanceProgress(false);
}
}
}