package org.basex.core.cmd; import static org.basex.core.Text.ADD; import static org.basex.core.Text.FILE_NOT_FOUND_X; import static org.basex.core.Text.NAME_INVALID_X; import static org.basex.core.Text.PATH_ADDED_X_X; import static org.basex.core.Text.TO; import java.io.IOException; import javax.xml.transform.sax.SAXSource; import org.basex.build.Builder; import org.basex.build.DirParser; import org.basex.build.DiskBuilder; import org.basex.build.MemBuilder; import org.basex.build.Parser; import org.basex.build.xml.SAXWrapper; import org.basex.core.CommandBuilder; import org.basex.core.Prop; import org.basex.core.User; import org.basex.data.Data; import org.basex.data.MetaData; import org.basex.io.IO; import org.basex.io.IOContent; import org.basex.util.Performance; import org.basex.util.Util; /** * Evaluates the 'add' command and adds a document to a collection.<br/> * Note that the constructors of this class have changed with Version 7.0: * the target path and file name have been merged and are now specified * as first argument. * * @author BaseX Team 2005-12, BSD License * @author Christian Gruen */ public final class Add extends ACreate { /** Builder. */ private Builder build; /** * Constructor, specifying a target path. * Note that the constructors of this class have changed with Version 7.0: * the target path and file name have been merged and are now specified * as first argument. * @param path target path, optionally terminated by a new file name */ public Add(final String path) { this(path, null); } /** * Constructor, specifying a target path and an input. * Note that the constructors of this class have changed with Version 7.0: * the target path and file name have been merged and are now specified * as first argument. * @param path target path, optionally terminated by a new file name. * If {@code null}, the name of the input will be set as path. * @param input input file or XML string */ public Add(final String path, final String input) { super(DATAREF | User.WRITE, path == null ? "" : path, input); } @Override protected boolean run() { final boolean create = context.user.perm(User.CREATE); String name = MetaData.normPath(args[0]); if(name == null || name.endsWith(".")) return error(NAME_INVALID_X, args[0]); // add slash to the target if the addressed file is an archive or directory IO io = null; if(in == null) { io = IO.get(args[1]); } else if(in.getSystemId() != null) { io = IO.get(in.getSystemId()); } else if(in.getByteStream() != null) { try { io = cache(); } catch(final IOException ex) { return error(Util.message(ex)); } } if(io != null) { if(!io.exists()) return error(FILE_NOT_FOUND_X, create ? io : args[1]); if(!name.endsWith("/") && (io.isDir() || io.isArchive())) name += '/'; } String target = ""; final int s = name.lastIndexOf('/'); if(s != -1) { target = name.substring(0, s); name = name.substring(s + 1); } final Data data = context.data(); final Parser parser; if(io != null) { // set name of document if(!name.isEmpty()) io.name(name); // get name from io reference else if(!(io instanceof IOContent)) name = io.name(); parser = new DirParser(io, target, prop, data.meta.path); } else { parser = new SAXWrapper(new SAXSource(in), name, target, context.prop); } // ensure that the final name is not empty if(name.isEmpty()) return error(NAME_INVALID_X, name); // create disk instances for large documents // (does not work for input streams and directories) final long fl = parser.src.length(); boolean large = false; final Runtime rt = Runtime.getRuntime(); if(fl > rt.freeMemory() / 3) { Performance.gc(2); large = fl > rt.freeMemory() / 3; } // in main memory mode, never write to disk if(prop.is(Prop.MAINMEM)) large = false; // create random database name for disk-based creation final String db = large ? context.mprop.random(data.meta.name) : name; build = large ? new DiskBuilder(db, parser, context) : new MemBuilder(db, parser, context.prop); Data tmp = null; try { tmp = build.build(); // ignore empty fragments if(tmp.meta.size > 1) { data.insert(data.meta.size, -1, tmp); context.update(); data.flush(); } return info(parser.info() + PATH_ADDED_X_X, name, perf); } catch(final IOException ex) { Util.debug(ex); return error(Util.message(ex)); } finally { // close and drop intermediary database instance try { build.close(); } catch(final IOException e) { } if(tmp != null) try { tmp.close(); } catch(final IOException e) { } // drop temporary database instance if(large) DropDB.drop(db, context.mprop); } } @Override public void build(final CommandBuilder cb) { cb.init().arg(TO, 0).arg(1); } @Override protected String tit() { return ADD; } @Override protected double prog() { return build != null ? build.prog() : 0; } }