package org.basex.query.up.primitives; import static org.basex.query.util.Err.*; import static org.basex.util.Token.*; import java.io.IOException; import java.util.Iterator; import org.basex.build.DirParser; import org.basex.build.MemBuilder; import org.basex.core.Context; import org.basex.data.Data; import org.basex.data.MemData; import org.basex.io.IO; import org.basex.io.IOContent; import org.basex.query.QueryException; import org.basex.query.item.ANode; import org.basex.query.item.Item; import org.basex.query.item.NodeType; import org.basex.query.item.Type; import org.basex.query.util.DataBuilder; import org.basex.util.InputInfo; import org.basex.util.Util; import org.basex.util.list.ObjList; import org.basex.util.list.TokenList; /** * Add primitive. * * @author BaseX Team 2005-12, BSD License * @author Dimitar Popov */ public final class DBAdd extends InsertBase { /** Documents to add. */ private ObjList<Item> docs = new ObjList<Item>(); /** Paths to which the new document(s) will be added. */ private TokenList paths = new TokenList(); /** Database context. */ private final Context ctx; /** * Constructor. * @param d target database * @param i input info * @param it document to add * @param p document(s) path * @param c database context */ public DBAdd(final Data d, final InputInfo i, final Item it, final String p, final Context c) { super(PrimitiveType.INSERTAFTER, -1, d, i, null); docs.add(it); paths.add(token(p)); ctx = c; } @Override public boolean adjacentTexts(final int c) { return false; } @Override public void merge(final UpdatePrimitive u) { final DBAdd a = (DBAdd) u; final Iterator<Item> d = a.docs.iterator(); final Iterator<byte[]> p = a.paths.iterator(); while(d.hasNext()) { docs.add(d.next()); paths.add(p.next()); } } @Override public void apply() { super.apply(); data.insert(data.meta.size, -1, md); } @Override public void prepare() throws QueryException { // build data with all documents, to prevent dirty reads md = new MemData(data); for(int i = 0; i < docs.size(); i++) { md.insert(md.meta.size, -1, docData(docs.get(i), paths.get(i))); docs.set(i, null); paths.set(i, null); size++; } docs = null; paths = null; } /** * Creates a {@link Data} instance for the specified document. * @param doc item representing document(s) * @param pth target path * @return {@link Data} instance from the parsed document(s) * @throws QueryException if {@code doc} does not represent valid document(s) */ private Data docData(final Item doc, final byte[] pth) throws QueryException { final MemData mdata; String name = string(pth); if(name.endsWith(".")) RESINV.thrw(input, pth); // add slash to the target if the addressed file is an archive or directory IO io = null; final Type dt = doc.type; if(dt.isString()) { io = IO.get(string(doc.string(input))); if(!io.exists()) RESFNF.thrw(input, pth); 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); } if(dt.isString()) { // set name of document if(!name.isEmpty()) io.name(name); // get name from io reference else if(!(io instanceof IOContent)) name = io.name(); } // ensure that the final name is not empty if(name.isEmpty()) RESINV.thrw(input, pth); if(dt.isNode()) { // adding a document node final ANode nd = (ANode) doc; if(nd.type != NodeType.DOC) UPDOCTYPE.thrw(input, nd); mdata = new MemData(data); new DataBuilder(mdata).build(nd); mdata.update(0, Data.DOC, pth); } else if(dt.isString()) { final DirParser p = new DirParser(io, target, ctx.prop, data.meta.path); final MemBuilder b = new MemBuilder(data.meta.name, p, ctx.prop); try { mdata = b.build(); } catch(final IOException ex) { throw IOERR.thrw(input, ex); } } else { throw STRNODTYPE.thrw(input, this, doc.type); } return mdata; } @Override public String toString() { return Util.name(this) + '[' + docs.get(0) + ']'; } }