package org.basex.gui.view.explore;
import static org.basex.core.Text.*;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import org.basex.core.cmd.Find;
import org.basex.data.Data;
import org.basex.gui.GUIProp;
import org.basex.gui.GUIConstants.Fill;
import org.basex.gui.layout.BaseXBack;
import org.basex.gui.layout.BaseXCombo;
import org.basex.gui.layout.BaseXDSlider;
import org.basex.gui.layout.BaseXLabel;
import org.basex.gui.layout.BaseXLayout;
import org.basex.gui.layout.BaseXPanel;
import org.basex.gui.layout.BaseXTextField;
import org.basex.gui.layout.TableLayout;
import org.basex.index.Names;
import org.basex.index.Stats;
import org.basex.util.Token;
import org.basex.util.TokenBuilder;
import org.basex.util.Util;
import org.basex.util.list.StringList;
import org.basex.util.list.TokenList;
/**
* This is a simple user search panel.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
* @author Bastian Lemke
*/
final class ExploreArea extends BaseXPanel implements ActionListener {
/** Component width. */
private static final int COMPW = 150;
/** Exact search pattern. */
private static final String PATEX = "[% = \"%\"]";
/** Substring search pattern. */
private static final String PATSUB = "[% contains text \"%\"]";
/** Numeric search pattern. */
private static final String PATNUM = "[% >= % and % <= %]";
/** Simple search pattern. */
private static final String PATSIMPLE = "[%]";
/** Main panel. */
private final ExploreView main;
/** Main panel. */
private final BaseXBack panel;
/** Query field. */
private final BaseXTextField all;
/** Last Query. */
private String last = "";
/**
* Default constructor.
* @param m main panel
*/
ExploreArea(final ExploreView m) {
super(m.gui);
main = m;
layout(new BorderLayout(0, 5)).mode(Fill.NONE);
all = new BaseXTextField(gui);
all.addKeyListener(main);
all.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(final KeyEvent e) {
query(false);
}
});
add(all, BorderLayout.NORTH);
panel = new BaseXBack(Fill.NONE).layout(new TableLayout(20, 2, 10, 5));
add(panel, BorderLayout.CENTER);
}
/**
* Initializes the panel.
*/
void init() {
panel.removeAll();
panel.revalidate();
panel.repaint();
}
@Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
final Data data = gui.context.data();
if(!main.visible() || data == null) return;
final boolean pi = data.meta.pathindex;
if(!pi || panel.getComponentCount() != 0) {
if(!pi) init();
return;
}
if(!pi) return;
addKeys(gui.context.data());
panel.revalidate();
panel.repaint();
}
/**
* Adds a text field.
* @param pos position
*/
private void addInput(final int pos) {
final BaseXTextField txt = new BaseXTextField(gui);
BaseXLayout.setWidth(txt, COMPW);
BaseXLayout.setHeight(txt, txt.getFont().getSize() + 11);
txt.setMargin(new Insets(0, 0, 0, 10));
txt.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(final KeyEvent e) {
query(false);
}
});
txt.addKeyListener(main);
panel.add(txt, pos);
}
/**
* Adds a category combobox.
* @param data data reference
*/
private void addKeys(final Data data) {
final TokenList tl = new TokenList();
final int cs = panel.getComponentCount();
for(int c = 0; c < cs; c += 2) {
final BaseXCombo combo = (BaseXCombo) panel.getComponent(c);
if(combo.getSelectedIndex() == 0) continue;
final String elem = combo.getSelectedItem().toString();
if(!elem.startsWith("@")) tl.add(Token.token(elem));
}
final TokenList tmp = data.paths.desc(tl, true, false);
final String[] keys = entries(tmp.toArray());
final BaseXCombo cm = new BaseXCombo(gui, keys);
cm.addActionListener(this);
cm.addKeyListener(main);
if(tmp.size() == 0) cm.setEnabled(false);
panel.add(cm);
panel.add(new BaseXLabel(""));
}
/**
* Adds a combobox.
* @param values combobox values
* @param pos position
*/
private void addCombo(final String[] values, final int pos) {
final BaseXCombo cm = new BaseXCombo(gui, values);
BaseXLayout.setWidth(cm, COMPW);
cm.addActionListener(this);
cm.addKeyListener(main);
panel.add(cm, pos);
}
/**
* Adds a combobox.
* @param min minimum value
* @param max maximum value
* @param pos position
* @param itr integer flag
*/
private void addSlider(final double min, final double max, final int pos,
final boolean itr) {
final BaseXDSlider sl = new BaseXDSlider(gui, min, max, this);
BaseXLayout.setWidth(sl, COMPW + BaseXDSlider.LABELW);
sl.itr = itr;
sl.addKeyListener(main);
panel.add(sl, pos);
}
@Override
public void actionPerformed(final ActionEvent e) {
if(e != null) {
final Object source = e.getSource();
// find modified component
int cp = 0;
final int cs = panel.getComponentCount();
for(int c = 0; c < cs; ++c) if(panel.getComponent(c) == source) cp = c;
if((cp & 1) == 0) {
// combo box with tags/attributes
final BaseXCombo combo = (BaseXCombo) source;
panel.remove(cp + 1);
final Data data = gui.context.data();
final boolean selected = combo.getSelectedIndex() != 0;
if(selected) {
final String item = combo.getSelectedItem().toString();
final boolean att = item.startsWith("@");
final Names names = att ? data.atnindex : data.tagindex;
final byte[] key = Token.token(att ? item.substring(1) : item);
final Stats stat = names.stat(names.id(key));
switch(stat.type) {
case INTEGER:
addSlider(stat.min, stat.max, cp + 1, true);
break;
case DOUBLE:
addSlider(stat.min, stat.max, cp + 1, false);
break;
case CATEGORY:
addCombo(entries(stat.cats.keys()), cp + 1);
break;
case TEXT:
addInput(cp + 1);
break;
case NONE:
panel.add(new BaseXLabel(""), cp + 1);
break;
}
} else {
panel.add(new BaseXLabel(""), cp + 1);
}
while(cp + 2 < panel.getComponentCount()) {
panel.remove(cp + 2);
panel.remove(cp + 2);
}
if(selected) addKeys(data);
panel.revalidate();
panel.repaint();
}
}
query(false);
}
/**
* Runs a query.
* @param force force the execution of a new query
*/
void query(final boolean force) {
final TokenBuilder tb = new TokenBuilder();
final Data data = gui.context.data();
final int cs = panel.getComponentCount();
for(int c = 0; c < cs; c += 2) {
final BaseXCombo com = (BaseXCombo) panel.getComponent(c);
final int k = com.getSelectedIndex();
if(k <= 0) continue;
String key = com.getSelectedItem().toString();
final boolean attr = key.startsWith("@");
if(key.contains(":")) key = key.replaceFirst("^.*:", "");
key = "*:" + key;
final Component comp = panel.getComponent(c + 1);
String pattern = "";
String val1 = null;
String val2 = null;
if(comp instanceof BaseXTextField) {
val1 = ((BaseXTextField) comp).getText();
if(!val1.isEmpty()) {
if(val1.startsWith("\"")) {
val1 = val1.replaceAll("\"", "");
pattern = PATEX;
} else {
pattern = attr && data.meta.attrindex ||
!attr && data.meta.textindex ? PATSUB : PATEX;
}
}
} else if(comp instanceof BaseXCombo) {
final BaseXCombo combo = (BaseXCombo) comp;
if(combo.getSelectedIndex() != 0) {
val1 = combo.getSelectedItem().toString();
pattern = PATEX;
}
} else if(comp instanceof BaseXDSlider) {
final BaseXDSlider slider = (BaseXDSlider) comp;
if(slider.min != slider.totMin || slider.max != slider.totMax) {
final double m = slider.min;
final double n = slider.max;
val1 = (long) m == m ? Long.toString((long) m) : Double.toString(m);
val2 = (long) n == n ? Long.toString((long) n) : Double.toString(n);
pattern = PATNUM;
}
}
if(attr) {
key = "descendant-or-self::node()/" + key;
if(tb.size() == 0) tb.add("//*");
if(pattern.isEmpty()) pattern = PATSIMPLE;
} else {
tb.add("//" + key);
key = "text()";
}
tb.addExt(pattern, key, val1, key, val2);
}
String qu = tb.toString();
final boolean root = gui.context.root();
final boolean rt = gui.gprop.is(GUIProp.FILTERRT);
if(!qu.isEmpty() && !rt && !root) qu = '.' + qu;
String simple = all.getText().trim();
if(!simple.isEmpty()) {
simple = Find.find(simple, gui.context, rt);
qu = !qu.isEmpty() ? simple + " | " + qu : simple;
}
if(qu.isEmpty()) qu = rt || root ? "/" : ".";
if(!force && last.equals(qu)) return;
last = qu;
gui.xquery(qu, false);
}
/**
* Returns the combo box selections
* and the keys of the specified set.
* @param key keys
* @return key array
*/
private static String[] entries(final byte[][] key) {
final StringList sl = new StringList();
sl.add(Util.info(ENTRIES, key.length));
for(final byte[] k : key) sl.add(Token.string(k));
sl.sort(true, true, 1);
return sl.toArray();
}
}