package org.basex.gui.view.editor; import org.basex.core.Prop; import org.basex.core.cmd.XQuery; import org.basex.gui.GUIProp; import org.basex.gui.layout.BaseXEditor; import static org.basex.gui.layout.BaseXKeys.*; import org.basex.gui.layout.BaseXLabel; import org.basex.io.IOFile; import org.basex.query.QueryContext; import org.basex.query.QueryException; import org.basex.query.QueryProcessor; import org.basex.util.Performance; import static org.basex.util.Token.*; import static org.basex.core.Text.*; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.io.IOException; /** * This class extends the text editor by XQuery features. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ final class EditorArea extends BaseXEditor { /** File label. */ final BaseXLabel label; /** File in tab. */ IOFile file; /** Timestamp. */ long tstamp; /** Flag for modified content. */ boolean modified; /** Thread counter. */ int threadID; /** Last input. */ byte[] last = EMPTY; /** This flag indicates if the input is an executable XQuery main module. */ boolean executable = true; /** View reference. */ private final EditorView view; /** * Constructor. * @param v view reference * @param f file reference */ EditorArea(final EditorView v, final IOFile f) { super(true, v.gui); view = v; file = f; label = new BaseXLabel(f.name()); setSyntax(f); addFocusListener(new FocusAdapter() { @Override public void focusGained(final FocusEvent e) { if(opened() && !modified) { try { // reload file that has been modified final long t = tstamp; if(file.timeStamp() != t) { setText(new IOFile(file.path()).read()); tstamp = t; } } catch(final IOException ex) { /* ignored */ } } } }); } /** * Returns {@code true} if a file has been opened from disk * (i.e., has a valid timestamp). * @return result of check */ boolean opened() { return tstamp != 0; } @Override public void setText(final byte[] t) { super.setText(t); last = t; } @Override public void keyPressed(final KeyEvent e) { super.keyPressed(e); if(Character.isDefined(e.getKeyChar())) { error(-1); } else { view.pos.setText(pos()); } } @Override public void mouseReleased(final MouseEvent e) { super.mouseReleased(e); view.pos.setText(pos()); } @Override public void keyReleased(final KeyEvent e) { super.keyReleased(e); if(!e.isActionKey() && !modifier(e)) release(EXEC.is(e)); } @Override protected void release(final boolean force) { final byte[] in = getText(); final boolean eq = eq(in, last); if(eq && !force) return; last = in; view.refresh(modified || !eq, false); view.pos.setText(pos()); gui.context.prop.set(Prop.QUERYPATH, file.path()); if(file.isXML()) { // non-executable input view.info(OK, true); executable = false; } else { // check if input is/might be an xquery main module final String qu = in.length == 0 ? "()" : string(in); executable = !module(in); if(executable && (force || gui.gprop.is(GUIProp.EXECRT))) { // execute query if forced, or if realtime execution is activated gui.execute(true, new XQuery(qu)); } else { // parse query final QueryContext ctx = new QueryContext(gui.context); try { if(!executable) ctx.module(qu); else ctx.parse(qu); view.info(OK, true); } catch(final QueryException ex) { view.info(ex.getMessage(), false); } } } } /** * Updates the editor. */ void update() { release(true); } /** * Performs the current query. */ void query() { release(true); } /** * Returns the currently assigned file. * @return file */ IOFile file() { return file; } /** * Sets the file reference. * @param f file */ void file(final IOFile f) { file = f; tstamp = f.timeStamp(); setSyntax(file); } /** * Highlights the error. * @param pos error position * @param cursor move cursor to error position */ void markError(final int pos, final boolean cursor) { if(cursor) { requestFocusInWindow(); setCaret(pos); } final int thread = ++threadID; final int sleep = pos == -1 ? 0 : 500; new Thread() { @Override public void run() { Performance.sleep(sleep); if(thread == threadID) error(pos); } }.start(); } /** * Verifies if the specified query is a module. * @param qu query to check * @return result of check */ private static boolean module(final byte[] qu) { return QueryProcessor.removeComments(string(qu), 20).startsWith( "module namespace "); } }