package org.xenei.jdbc4sparql.impl;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.apache.commons.lang.StringUtils;
import org.apache.jena.riot.RDFDataMgr;
import org.apache.jena.riot.RDFFormat;
import org.apache.xerces.util.XMLChar;
import org.xenei.jdbc4sparql.J4SPropertyNames;
import org.xenei.jdbc4sparql.iface.DatasetProducer;
import org.xenei.jdbc4sparql.impl.rdf.RdfCatalog;
import org.xenei.jdbc4sparql.utils.NoCloseZipInputStream;
import com.hp.hpl.jena.query.Dataset;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.impl.Util;
/**
* Interface that defines the dataset producer.
*
* The dataset producer produces the local dataset (set of graphs that represent
* the local data) and the meta dataset (set of graphs that contain the
* metadata)
*
* Implementations of this class should construct the dataset when first
* requested and return the same dataset on all subsequent calls.
*/
abstract public class AbstractDatasetProducer implements DatasetProducer {
public static String getModelURI(final String modelName) {
String name = StringUtils.defaultString(modelName);
if (StringUtils.isEmpty(name)) {
name = RdfCatalog.Builder.getFQName(name);
}
else {
final int i = Util.splitNamespace(name);
if (i == 1) {
if (XMLChar.isNCNameStart(name.charAt(0))) {// we have a short
// name
name = RdfCatalog.Builder.getFQName(name);
}
}
}
return name;
}
private final Properties properties;
private final RDFFormat format = RDFFormat.TRIG;
protected Dataset localData;
protected Dataset metaData;
protected AbstractDatasetProducer(final Properties properties,
final Dataset metaDataset, final Dataset localDataset) {
this.properties = properties;
this.metaData = metaDataset;
this.localData = localDataset;
}
@Override
public void addLocalDataModel(final String modelName, final Model model) {
final String name = AbstractDatasetProducer.getModelURI(modelName);
getLocalDataset().addNamedModel(name, model);
}
/**
* Close the datasets in preparation for shutdown.
*/
@Override
public void close() {
getMetaDataset().close();
getLocalDataset().close();
}
private String createFN(final String prefix) {
return String.format("%s.%s", prefix, format.getLang()
.getFileExtensions().get(0));
}
@Override
public Model getLocalDataModel(final String modelName) {
return getModel(getLocalDataset(), modelName);
}
/**
* Get or construct the local dataset.
*
* @return the local dataset
*/
protected Dataset getLocalDataset() {
return localData;
}
@Override
public Model getMetaDataModel(final String modelName) {
return getModel(getMetaDataset(), modelName);
}
/**
* Get or construct the meta dataset.
*
* @return the meta dataset.
*/
protected Dataset getMetaDataset() {
return metaData;
}
/**
* Retrieve the model that is the union of all models in the data set.
*
* @return
*/
@Override
public Model getMetaDatasetUnionModel() {
return getMetaDataset().getNamedModel("urn:x-arq:UnionGraph");
}
private Model getModel(final Dataset dataset, final String modelName) {
final String name = AbstractDatasetProducer.getModelURI(modelName);
final Model model = dataset.getNamedModel(name);
if (!dataset.containsNamedModel(name)) {
dataset.addNamedModel(name, model);
}
return model;
}
@Override
public Properties getProperties() {
return new Properties(properties);
}
@Override
public Iterator<String> listMetaDataNames() {
return getMetaDataset().listNames();
}
/**
* Default load implementation
*
* @param zis
* @throws IOException
*/
protected void load(final ZipInputStream zis) throws IOException {
ZipEntry e = zis.getNextEntry();
if (e.getName().startsWith(DatasetProducer.META_PREFIX)) {
loadMeta(zis, e);
}
else {
throw new IllegalStateException("Entry must start with "
+ DatasetProducer.META_PREFIX);
}
e = zis.getNextEntry();
if (e.getName().startsWith(DatasetProducer.LOCAL_PREFIX)) {
loadLocal(zis, e);
}
else {
throw new IllegalStateException("Entry must start with "
+ DatasetProducer.LOCAL_PREFIX);
}
}
private void loadDataset(final ZipInputStream zis, final ZipEntry e,
final Dataset ds, final String prefix) {
if (e.getName().equals(createFN(prefix))) {
RDFDataMgr.read(ds, new NoCloseZipInputStream(zis),
format.getLang());
}
else {
throw new IllegalArgumentException("Entry name must be "
+ createFN(prefix));
}
}
protected void loadLocal(final ZipInputStream zis, final ZipEntry e) {
loadDataset(zis, e, localData, DatasetProducer.LOCAL_PREFIX);
}
protected void loadMeta(final ZipInputStream zis, final ZipEntry e) {
loadDataset(zis, e, metaData, DatasetProducer.META_PREFIX);
}
@Override
final public void save(final File f) throws FileNotFoundException,
IOException {
save(new FileOutputStream(f));
}
@Override
final public void save(final OutputStream out) throws IOException {
final ZipOutputStream zos = new ZipOutputStream(out);
try {
properties.setProperty(J4SPropertyNames.DATASET_PRODUCER, this
.getClass().getCanonicalName());
final ZipEntry e = new ZipEntry(
DatasetProducer.PROPERTIES_ENTRY_NAME);
zos.putNextEntry(e);
properties.store(zos, "");
zos.closeEntry();
saveMeta(zos);
saveLocal(zos);
} finally {
zos.close();
}
}
private void saveDataset(final ZipOutputStream out, final Dataset ds,
final String prefix) throws IOException {
final ZipEntry e = new ZipEntry(createFN(prefix));
out.putNextEntry(e);
RDFDataMgr.write(out, ds, format);
out.closeEntry();
}
protected void saveLocal(final ZipOutputStream out) throws IOException {
saveDataset(out, getLocalDataset(), DatasetProducer.LOCAL_PREFIX);
}
protected void saveMeta(final ZipOutputStream out) throws IOException {
saveDataset(out, getMetaDataset(), DatasetProducer.META_PREFIX);
}
}