package org.basex.core.cmd;
import static org.basex.core.Text.DB_CREATED_X_X;
import static org.basex.core.Text.DB_PINNED_X;
import static org.basex.core.Text.NAME_INVALID_X;
import static org.basex.core.Text.NOT_PARSED_X;
import static org.basex.data.DataText.DATAATV;
import static org.basex.data.DataText.DATAFTX;
import static org.basex.data.DataText.DATATXT;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.basex.build.DiskBuilder;
import org.basex.build.MemBuilder;
import org.basex.build.Parser;
import org.basex.core.Command;
import org.basex.core.Context;
import org.basex.core.ProgressException;
import org.basex.core.Prop;
import org.basex.core.User;
import org.basex.data.Data;
import org.basex.data.DataText;
import org.basex.data.MemData;
import org.basex.data.MetaData;
import org.basex.index.IndexBuilder;
import org.basex.index.IndexToken.IndexType;
import org.basex.index.ft.FTBuilder;
import org.basex.index.path.PathBuilder;
import org.basex.index.value.ValueBuilder;
import org.basex.io.IOContent;
import org.basex.util.Util;
import org.basex.util.list.ByteList;
/**
* Abstract class for database creation commands.
*
* @author BaseX Team 2005-12, BSD License
* @author Christian Gruen
*/
public abstract class ACreate extends Command {
/** Flag for creating new data instances. */
private boolean closing;
/**
* Protected constructor, specifying command arguments.
* @param arg arguments
*/
ACreate(final String... arg) {
this(User.CREATE, arg);
closing = true;
}
/**
* Protected constructor, specifying command flags and arguments.
* @param flags command flags
* @param arg arguments
*/
ACreate(final int flags, final String... arg) {
super(flags, arg);
}
@Override
public boolean newData(final Context ctx) {
if(closing) new Close().run(ctx);
return closing;
}
@Override
public final boolean supportsProg() {
return true;
}
@Override
public boolean stoppable() {
return true;
}
/**
* Builds and creates a new database instance.
* @param parser parser instance
* @param db name of database
* @return success of operation
*/
final boolean build(final Parser parser, final String db) {
if(!MetaData.validName(db, false)) return error(NAME_INVALID_X, db);
// close open database
new Close().run(context);
try {
if(context.pinned(db)) return error(DB_PINNED_X, db);
// database builder instance.
if(prop.is(Prop.MAINMEM)) {
final Data data = progress(new MemBuilder(db, parser, prop)).build();
context.openDB(data);
context.pin(data);
} else {
Data data = progress(new DiskBuilder(db, parser, context)).build();
data.close();
final Open open = new Open(db);
if(!open.run(context)) return error(open.info());
data = context.data();
data.meta.pathindex = data.meta.createpath;
if(data.meta.createtext) create(IndexType.TEXT, data, this);
if(data.meta.createattr) create(IndexType.ATTRIBUTE, data, this);
if(data.meta.createftxt) create(IndexType.FULLTEXT, data, this);
data.flush();
}
return info(parser.info() + DB_CREATED_X_X, db, perf);
} catch(final ProgressException ex) {
throw ex;
} catch(final IOException ex) {
Util.debug(ex);
abort();
final String msg = ex.getMessage();
return error(msg != null && !msg.isEmpty() ? msg :
Util.info(NOT_PARSED_X, parser.src));
} catch(final Exception ex) {
// known exceptions:
// - IllegalArgumentException (UTF8, zip files)
Util.debug(ex);
abort();
return error(Util.info(NOT_PARSED_X, parser.src));
}
}
/**
* Returns cached input if the input is streamed and a data format different
* than XML has been chosen.
* @return cached input
* @throws IOException I/O exception
*/
protected IOContent cache() throws IOException {
if(in == null || prop.get(Prop.PARSER).equals(DataText.M_XML)) return null;
final InputStream is = in.getByteStream();
final BufferedInputStream bis = new BufferedInputStream(is);
final ByteList ao = new ByteList();
try {
for(int b; (b = bis.read()) != -1;) ao.add(b);
} catch(final IOException ex) {
Util.debug(ex);
throw ex;
} finally {
try { bis.close(); } catch(final IOException ex) { /* ignored */ }
}
return new IOContent(ao.toArray());
}
/**
* Builds the specified index.
* @param index index to be built
* @param data data reference
* @param cmd calling command
* @throws IOException I/O exception
*/
static void create(final IndexType index, final Data data,
final ACreate cmd) throws IOException {
if(data instanceof MemData) return;
final IndexBuilder ib;
switch(index) {
case TEXT: ib = new ValueBuilder(data, true); break;
case ATTRIBUTE: ib = new ValueBuilder(data, false); break;
case FULLTEXT: ib = FTBuilder.get(data); break;
case PATH: ib = new PathBuilder(data); break;
default: throw Util.notexpected();
}
data.closeIndex(index);
data.setIndex(index, (cmd == null ? ib : cmd.progress(ib)).build());
}
/**
* Drops the specified index.
* @param index index type
* @param data data reference
* @return success of operation
* @throws IOException I/O exception
*/
static boolean drop(final IndexType index, final Data data)
throws IOException {
String pat = null;
switch(index) {
case TEXT:
data.meta.textindex = false;
pat = DATATXT;
break;
case ATTRIBUTE:
data.meta.attrindex = false;
pat = DATAATV;
break;
case FULLTEXT:
data.meta.ftxtindex = false;
pat = DATAFTX;
break;
case PATH:
data.meta.pathindex = false;
break;
default:
}
data.closeIndex(index);
data.meta.dirty = true;
data.flush();
return pat == null || data.meta.drop(pat + '.');
}
}