@Override public Item item(final QueryContext qc, final InputInfo ii) throws QueryException { final Data data = checkData(qc); final String path = path(1, qc); final Item item = toItem(exprs[2], qc); final Options opts = toOptions(3, Q_OPTIONS, new Options(), qc); final Updates updates = qc.resources.updates(); final IntList docs = data.resources.docs(path); int d = 0; // delete binary resources final IOFile bin = data.meta.binary(path); if (bin == null || bin.isDir()) throw BXDB_REPLACE_X.get(info, path); if (item instanceof Bin) { updates.add(new DBStore(data, path, item, info), qc); } else { if (bin.exists()) updates.add(new DBDelete(data, path, info), qc); final NewInput input = checkInput(item, token(path)); if (docs.isEmpty() || docs.get(0) == 0) { // no replacement of first document (because of TableDiskAccess#insert, used > 0, pre = 0) updates.add(new DBAdd(data, input, opts, qc, info), qc); } else { updates.add(new ReplaceDoc(docs.get(0), data, input, opts, qc, info), qc); d = 1; } } // delete old documents final int ds = docs.size(); for (; d < ds; d++) updates.add(new DeleteNode(docs.get(d), data, info), qc); return null; }
/** * Returns the sorted names of all available databases and, optionally, backups. Filters for * {@code name} if not {@code null} with glob support. * * @param db return databases? * @param backup return backups? * @param name name filter (may be {@code null}) * @return database and backups list */ private StringList list(final boolean db, final boolean backup, final String name) { final Pattern pt; if (name != null) { final String nm = REGEX.matcher(name).matches() ? IOFile.regex(name) : name.replaceAll("([" + REGEXCHARS + "])", "\\\\$1"); pt = Pattern.compile(nm, Prop.CASE ? 0 : Pattern.CASE_INSENSITIVE); } else { pt = null; } final IOFile[] children = soptions.dbpath().children(); final StringList list = new StringList(children.length); final HashSet<String> map = new HashSet<>(children.length); for (final IOFile f : children) { final String fn = f.name(); String add = null; if (backup && fn.endsWith(IO.ZIPSUFFIX)) { final String nn = ZIPPATTERN.split(fn)[0]; if (!nn.equals(fn)) add = nn; } else if (db && f.isDir() && fn.indexOf('.') == -1) { add = fn; } // add entry if it matches the pattern, and has not already been added if (add != null && (pt == null || pt.matcher(add).matches()) && map.add(add)) { list.add(add); } } return list.sort(false); }
@Override public void execute(final GUI gui) { final DialogExport dialog = new DialogExport(gui); if (!dialog.ok()) return; final IOFile root = new IOFile(dialog.path()); // check if existing files will be overwritten if (root.exists()) { IO file = null; boolean overwrite = false; final Data d = gui.context.data(); final IntList il = d.resources.docs(); final int is = il.size(); for (int i = 0; i < is; i++) { file = root.merge(Token.string(d.text(il.get(i), true))); if (file.exists()) { if (overwrite) { // more than one file will be overwritten; check remaining tests file = null; break; } overwrite = true; } } if (overwrite) { // show message for overwriting files or directories final String msg = file == null ? FILES_REPLACE_X : FILE_EXISTS_X; if (file == null) file = root; if (!BaseXDialog.confirm(gui, Util.info(msg, file))) return; } } DialogProgress.execute(gui, new Export(root.path())); }
/** * Returns the names of all backups. * * @return backups */ public StringList backups() { final StringList backups = new StringList(); for (final IOFile f : soptions.dbpath().children()) { final String n = f.name(); if (n.endsWith(IO.ZIPSUFFIX)) backups.add(n.substring(0, n.lastIndexOf('.'))); } return backups; }
/** * Returns a list of all databases. * * @param ctx database context * @return list of databases */ public static StringList list(final Context ctx) { final StringList db = new StringList(); for (final IOFile f : ctx.mprop.dbpath().children()) { final String name = f.name(); if (f.isDir() && !name.startsWith(".")) db.add(name); } return db.sort(false); }
@Override public void startUpdate(final MainOptions opts) throws IOException { if (!table.lock(true)) throw new BaseXException(Text.DB_PINNED_X, meta.name); if (opts.get(MainOptions.AUTOFLUSH)) { final IOFile uf = meta.updateFile(); if (uf.exists()) throw new BaseXException(Text.DB_UPDATED_X, meta.name); if (!uf.touch()) throw Util.notExpected("%: could not create lock file.", meta.name); } }
/** Open all selected files externally. */ private void openExternal() { for (final IOFile file : selectedValues()) { try { file.open(); } catch (final IOException ex) { BaseXDialog.error(project.gui, Util.info(FILE_NOT_OPENED_X, file)); } } }
/** * Returns the name of a specific backup, or all backups found for a specific database, in a * descending order. * * @param db database * @return names of specified backups */ public StringList backups(final String db) { final StringList backups = new StringList(); final IOFile file = soptions.dbpath(db + IO.ZIPSUFFIX); if (file.exists()) { backups.add(db); } else { final String regex = db.replaceAll("([" + REGEXCHARS + "])", "\\\\$1") + DateTime.PATTERN + IO.ZIPSUFFIX; for (final IOFile f : soptions.dbpath().children()) { final String n = f.name(); if (n.matches(regex)) backups.add(n.substring(0, n.lastIndexOf('.'))); } } return backups.sort(Prop.CASE, false); }
/** * Chooses files that match the specified pattern. * * @param file file filter * @param content content filter * @param root root directory * @return sorted file paths * @throws InterruptedException interruption */ String[] filter(final String file, final String content, final IOFile root) throws InterruptedException { final long id = ++filterId; final TreeSet<String> results = new TreeSet<>(); final int[] search = new TokenParser(Token.lc(Token.token(content))).toArray(); // glob pattern final ProjectCache pc = cache(root); if (file.contains("*") || file.contains("?")) { final Pattern pt = Pattern.compile(IOFile.regex(file)); for (final String path : pc) { final int offset = offset(path, true); if (pt.matcher(path.substring(offset)).matches() && filterContent(path, search)) { results.add(path); if (results.size() >= MAXHITS) break; } if (id != filterId) throw new InterruptedException(); } } else { // starts-with, contains, camel case final String pttrn = file.toLowerCase(Locale.ENGLISH).replace('\\', '/'); final HashSet<String> exclude = new HashSet<>(); final boolean pathSearch = pttrn.indexOf('/') != -1; for (int i = 0; i < (pathSearch ? 2 : 3); i++) { filter(pttrn, search, i, results, exclude, pathSearch, pc, id); } } return results.toArray(new String[results.size()]); }
@Override public synchronized void finishUpdate(final MainOptions opts) { // remove updating file final boolean auto = opts.get(MainOptions.AUTOFLUSH); if (auto) { final IOFile uf = meta.updateFile(); if (!uf.exists()) throw Util.notExpected("%: lock file does not exist.", meta.name); if (!uf.delete()) throw Util.notExpected("%: could not delete lock file.", meta.name); } // db:optimize(..., true) will close the database before this function is called if (!closed) { flush(auto); if (!table.lock(false)) throw Util.notExpected("Database '%': could not unlock.", meta.name); } }
/** * Refreshes the list of recent query files and updates the query path. * * @param file new file */ void refreshHistory(final IOFile file) { final StringList sl = new StringList(); String path = null; if (file != null) { path = file.path(); gui.gprop.set(GUIProp.WORKPATH, file.dirPath()); sl.add(path); tabs.setToolTipTextAt(tabs.getSelectedIndex(), path); } final String[] qu = gui.gprop.strings(GUIProp.EDITOR); for (int q = 0; q < qu.length && q < 19; q++) { final String f = qu[q]; if (!f.equalsIgnoreCase(path) && IO.get(f).exists()) sl.add(f); } // store sorted history gui.gprop.set(GUIProp.EDITOR, sl.toArray()); hist.setEnabled(!sl.isEmpty()); }
/** * Refreshes the view after a file has been saved. * * @param root root directory * @param ctx database context * @throws InterruptedException interruption */ void parse(final IOFile root, final Context ctx) throws InterruptedException { final long id = ++parseId; final HashSet<String> parsed = new HashSet<>(); final TreeMap<String, InputInfo> errs = new TreeMap<>(); // collect files to be parsed final ProjectCache pc = cache(root); final StringList mods = new StringList(), lmods = new StringList(); for (final String path : pc) { final IOFile file = new IOFile(path); if (file.hasSuffix(IO.XQSUFFIXES)) (file.hasSuffix(IO.XQMSUFFIX) ? lmods : mods).add(path); } mods.add(lmods); // parse modules for (final String path : mods) { if (id != parseId) throw new InterruptedException(); if (parsed.contains(path)) continue; final IOFile file = new IOFile(path); try (final TextInput ti = new TextInput(file)) { // parse query try (final QueryContext qc = new QueryContext(ctx)) { final String input = ti.cache().toString(); final boolean lib = QueryProcessor.isLibrary(input); qc.parse(input, lib, path, null); // parsing was successful: remember path parsed.add(path); for (final byte[] mod : qc.modParsed) parsed.add(Token.string(mod)); } catch (final QueryException ex) { // parsing failed: remember path final InputInfo ii = ex.info(); errs.put(path, ii); parsed.add(ii.path()); } } catch (final IOException ex) { // file may not be accessible Util.debug(ex); } } errors = errs; }
/** * Saves the specified editor contents. * * @param file file to write * @return {@code false} if confirmation was canceled */ private boolean save(final IOFile file) { try { final EditorArea edit = getEditor(); file.write(edit.getText()); edit.file(file); return true; } catch (final IOException ex) { BaseXDialog.error(gui, FILE_NOT_SAVED); return false; } }
/** * Lists all databases. * * @return success flag * @throws IOException I/O exception */ private boolean list() throws IOException { final Table table = new Table(); table.description = DATABASES_X; final boolean create = context.user.has(Perm.CREATE); table.header.add(T_NAME); table.header.add(RESOURCES); table.header.add(SIZE); if (create) table.header.add(INPUT_PATH); for (final String name : context.databases.listDBs()) { String file = null; long size = 0; int docs = 0; final MetaData meta = new MetaData(name, context); try { meta.read(); size = meta.dbsize(); docs = meta.ndocs; if (context.perm(Perm.READ, meta)) file = meta.original; } catch (final IOException ex) { file = ERROR; } // count number of raw files final IOFile dir = new IOFile(mprop.dbpath(name), M_RAW); final int bin = dir.descendants().size(); // create entry if (file != null) { final TokenList tl = new TokenList(4); tl.add(name); tl.add(docs + bin); tl.add(size); if (create) tl.add(file); table.contents.add(tl); } } out.println(table.sort().finish()); return true; }
@Override public void action(final Object comp) { final String pth = path(); final IOFile io = new IOFile(pth); String inf = io.isDir() && io.children().length > 0 ? DIR_NOT_EMPTY : null; ok = !pth.isEmpty(); final SerialMethod mth = SerialMethod.valueOf(method.getSelectedItem()); final OptionsOption<? extends Options> opts = mth == SerialMethod.JSON ? SerializerOptions.JSON : mth == SerialMethod.CSV ? SerializerOptions.CSV : null; final boolean showmparams = opts != null; mparams.setEnabled(showmparams); if (ok) { gui.gopts.set(GUIOptions.INPUTPATH, pth); try { if (comp == method) { if (showmparams) { final Options mopts = options(null).get(opts); mparams.setToolTipText(tooltip(mopts)); mparams.setText(mopts.toString()); } else { mparams.setToolTipText(null); mparams.setText(""); } } Serializer.get(new ArrayOutput(), options(mth)); } catch (final IOException ex) { ok = false; inf = ex.getMessage(); } } info.setText(inf, ok ? Msg.WARN : Msg.ERROR); enableOK(buttons, B_OK, ok); }
/** * Recursively populates the cache. * * @param root root directory * @param pc file cache * @throws InterruptedException interruption */ void add(final IOFile root, final ProjectCache pc) throws InterruptedException { // check if file cache was replaced or invalidated if (pc != cache) throw new InterruptedException(); final IOFile[] files = root.children(); for (final IOFile file : files) { if (file.name().equals(IO.IGNORESUFFIX)) return; } for (final IOFile file : files) { if (file.isDir()) { add(file, pc); } else { pc.add(file.path()); } } }
/** * Opens the specified query file. * * @param file query file * @return opened editor */ public EditorArea open(final IOFile file) { if (!visible()) GUICommands.C_SHOWEDITOR.execute(gui); EditorArea edit = find(file, true); try { if (edit != null) { // display open file tabs.setSelectedComponent(edit); edit.reopen(true); } else { // get current editor edit = getEditor(); // create new tab if current text is stored on disk or has been modified if (edit.opened() || edit.modified) edit = addTab(); edit.initText(file.read()); edit.file(file); } } catch (final IOException ex) { BaseXDialog.error(gui, FILE_NOT_OPENED); } return edit; }
@Override public B64 item(final QueryContext qc, final InputInfo ii) throws QueryException { checkCreate(qc); final IOFile root = new IOFile(toPath(0, qc).toString()); final ArchOptions opts = toOptions(1, Q_OPTIONS, new ArchOptions(), qc); final Iter entries; if (exprs.length > 2) { entries = qc.iter(exprs[2]); } else { final TokenList tl = new TokenList(); for (final String file : root.descendants()) tl.add(file); entries = StrSeq.get(tl).iter(); } final String format = opts.get(ArchOptions.FORMAT); final int level = level(opts); if (!root.isDir()) throw FILE_NO_DIR_X.get(info, root); try (final ArchiveOut out = ArchiveOut.get(format.toLowerCase(Locale.ENGLISH), info)) { out.level(level); try { while (true) { Item en = entries.next(); if (en == null) break; en = checkElemToken(en); final IOFile file = new IOFile(root, string(en.string(info))); if (!file.exists()) throw FILE_NOT_FOUND_X.get(info, file); if (file.isDir()) throw FILE_IS_DIR_X.get(info, file); add(en, new B64(file.read()), out, level, qc); } } catch (final IOException ex) { throw ARCH_FAIL_X.get(info, ex); } return new B64(out.finish()); } }
/** Writes the properties to disk. */ public final synchronized void write() { final StringBuilder user = new StringBuilder(); BufferedReader br = null; try { // caches options specified by the user if (file.exists()) { br = new BufferedReader(new FileReader(file.file())); for (String line; (line = br.readLine()) != null; ) { if (line.equals(PROPUSER)) break; } for (String line; (line = br.readLine()) != null; ) { user.append(line).append(NL); } } } catch (final Exception ex) { Util.debug(ex); } finally { if (br != null) try { br.close(); } catch (final IOException e) { } } BufferedWriter bw = null; try { bw = new BufferedWriter(new FileWriter(file.file())); bw.write(PROPHEADER + NL); for (final Field f : getClass().getFields()) { final Object obj = f.get(null); if (!(obj instanceof Object[])) continue; final String key = ((Object[]) obj)[0].toString(); final Object val = props.get(key); if (val instanceof String[]) { final String[] str = (String[]) val; bw.write(key + " = " + str.length + NL); final int is = str.length; for (int i = 0; i < is; ++i) { if (str[i] != null) bw.write(key + (i + 1) + " = " + str[i] + NL); } } else if (val instanceof int[]) { final int[] num = (int[]) val; final int ns = num.length; for (int i = 0; i < ns; ++i) { bw.write(key + i + " = " + num[i] + NL); } } else { bw.write(key + " = " + val + NL); } } bw.write(NL + PROPUSER + NL); bw.write(user.toString()); } catch (final Exception ex) { Util.errln("% could not be written.", file); Util.debug(ex); } finally { if (bw != null) try { bw.close(); } catch (final IOException e) { } } }
/** Opens a file dialog to choose an XML document or directory. */ private void choose() { final IOFile io = new BaseXFileChooser(CHOOSE_DIR, path.getText(), gui).select(Mode.DOPEN); if (io != null) path.setText(io.path()); }
/** Tests all files. */ private void test() { for (final IOFile file : selectedValues()) { project.gui.execute(new Test(file.path())); } }
/** * Reads the configuration file and initializes the project properties. The file is located in the * project home directory. * * @param prop property file extension */ protected synchronized void read(final String prop) { file = new IOFile(HOME + IO.BASEXSUFFIX + prop); final StringList read = new StringList(); final TokenBuilder err = new TokenBuilder(); if (!file.exists()) { err.addExt("Saving properties in \"%\"..." + NL, file); } else { BufferedReader br = null; try { br = new BufferedReader(new FileReader(file.file())); for (String line; (line = br.readLine()) != null; ) { line = line.trim(); if (line.isEmpty() || line.charAt(0) == '#') continue; final int d = line.indexOf('='); if (d < 0) { err.addExt("%: \"%\" ignored. " + NL, file, line); continue; } final String val = line.substring(d + 1).trim(); String key = line.substring(0, d).trim(); // extract numeric value in key int num = 0; final int ss = key.length(); for (int s = 0; s < ss; ++s) { if (Character.isDigit(key.charAt(s))) { num = Integer.parseInt(key.substring(s)); key = key.substring(0, s); break; } } read.add(key); final Object entry = props.get(key); if (entry == null) { err.addExt("%: \"%\" not found. " + NL, file, key); } else if (entry instanceof String) { props.put(key, val); } else if (entry instanceof Integer) { props.put(key, Integer.parseInt(val)); } else if (entry instanceof Boolean) { props.put(key, Boolean.parseBoolean(val)); } else if (entry instanceof String[]) { if (num == 0) { props.put(key, new String[Integer.parseInt(val)]); } else { ((String[]) entry)[num - 1] = val; } } else if (entry instanceof int[]) { ((int[]) entry)[num] = Integer.parseInt(val); } } } catch (final Exception ex) { err.addExt("% could not be parsed." + NL, file); Util.debug(ex); } finally { if (br != null) try { br.close(); } catch (final IOException ex) { } } } // check if all mandatory files have been read try { if (err.isEmpty()) { boolean ok = true; for (final Field f : getClass().getFields()) { final Object obj = f.get(null); if (!(obj instanceof Object[])) continue; final String key = ((Object[]) obj)[0].toString(); ok &= read.contains(key); } if (!ok) err.addExt("Saving properties in \"%\"..." + NL, file); } } catch (final IllegalAccessException ex) { Util.notexpected(ex); } if (!err.isEmpty()) { Util.err(err.toString()); write(); } }