/*
 * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2007.
 * 
 * Licensed under the Aduna BSD-style license.
 */
package org.openrdf.rdf2go;

import info.aduna.iteration.CloseableIteration;
import info.aduna.iteration.Iterations;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.ontoware.aifbcommons.collection.ClosableIterable;
import org.ontoware.aifbcommons.collection.ClosableIterator;
import org.ontoware.rdf2go.exception.LockException;
import org.ontoware.rdf2go.exception.ModelRuntimeException;
import org.ontoware.rdf2go.exception.QueryLanguageNotSupportedException;
import org.ontoware.rdf2go.exception.SyntaxNotSupportedException;
import org.ontoware.rdf2go.model.Diff;
import org.ontoware.rdf2go.model.DiffReader;
import org.ontoware.rdf2go.model.Model;
import org.ontoware.rdf2go.model.QueryResultTable;
import org.ontoware.rdf2go.model.Statement;
import org.ontoware.rdf2go.model.Syntax;
import org.ontoware.rdf2go.model.impl.AbstractLockingModel;
import org.ontoware.rdf2go.model.node.BlankNode;
import org.ontoware.rdf2go.model.node.Node;
import org.ontoware.rdf2go.model.node.NodeOrVariable;
import org.ontoware.rdf2go.model.node.Resource;
import org.ontoware.rdf2go.model.node.ResourceOrVariable;
import org.ontoware.rdf2go.model.node.URI;
import org.ontoware.rdf2go.model.node.UriOrVariable;
import org.ontoware.rdf2go.model.node.impl.URIImpl;
import org.openrdf.OpenRDFException;
import org.openrdf.model.Namespace;
import org.openrdf.model.Value;
import org.openrdf.model.ValueFactory;
import org.openrdf.query.GraphQueryResult;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.UnsupportedQueryLanguageException;
import org.openrdf.repository.Repository;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.RepositoryResult;
import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParseException;
import org.openrdf.rio.RDFWriter;
import org.openrdf.rio.Rio;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Implementation of the RDF2Go model interface for an OpenRDF Repository.
 * 
 * Note that RepositoryModel and RepositoryModelSet only work well together
 * because they both keep their RepositoryConnections in auto-commit mode. This
 * cannot be changed by the user. Do mass-updates using
 * {@link #update(DiffReader)}, {@link #addAll(Iterator)} or
 * {@link #removeAll(Iterator)}, then the current connection will be used in
 * non-autocommit mode and commited, including a rollback when it fails.
 */
public class RepositoryModel extends AbstractLockingModel implements Model {
	
	private static final long serialVersionUID = 1466969214320765429L;
	
	private static Logger log = LoggerFactory.getLogger(RepositoryModel.class);
	
	public static final String DEFAULT_CONTEXT = "urn:nullcontext";
	
	public static final org.openrdf.model.URI DEFAULT_OPENRDF_CONTEXT = null;
	
	final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	protected Repository repository;
	
	protected RepositoryConnection connection;
	
	protected ValueFactory valueFactory;
	
	private boolean locked = false;
	
	protected URI context;
	
	private org.openrdf.model.URI openRdfContext;
	
	public RepositoryModel(Repository repository) throws ModelRuntimeException {
		if(repository == null) {
			throw new IllegalArgumentException("Repository cannot be null");
		}
		
		this.repository = repository;
		init();
	}
	
	public RepositoryModel(URI context, Repository repository) throws ModelRuntimeException {
		if(repository == null) {
			throw new IllegalArgumentException("Repository cannot be null");
		}
		
		this.repository = repository;
		this.context = context;
		init();
	}
	
	private void init() {
		this.valueFactory = this.repository.getValueFactory();
		
		if(this.context == null) {
			this.context = new URIImpl(DEFAULT_CONTEXT, false);
			this.openRdfContext = DEFAULT_OPENRDF_CONTEXT;
		} else {
			this.openRdfContext = this.valueFactory.createURI(this.context.toString());
		}
	}
	
	/**
	 * Returns the context as a OpenRDF URI.
	 */
	public org.openrdf.model.URI getOpenRDFContextURI() {
		return this.openRdfContext;
	}
	
	@Override
	public Model open() {
		// establish a connection only if none had been established
		if(isOpen()) {
			return this;
		}
		try {
			this.connection = this.repository.getConnection();
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
		return this;
	}
	
	@Override
	public boolean isOpen() throws ModelRuntimeException {
		try {
			return this.connection != null && this.connection.isOpen();
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	/**
	 * Closes the Connection to the wrapper Repository.
	 */
	@Override
	public void close() {
		try {
			if(isOpen()) {
				this.connection.close();
				this.connection = null;
			}
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	public BlankNode createBlankNode() {
		return new OpenrdfBlankNode(this.valueFactory.createBNode());
	}
	
	@Override
	public BlankNode createBlankNode(String internalID) {
		return new OpenrdfBlankNode(this.valueFactory.createBNode(internalID));
	}
	
	@Override
	public boolean isValidURI(String uriString) {
		boolean isValid = true;
		try {
			this.valueFactory.createURI(uriString);
		} catch(IllegalArgumentException e) {
			isValid = false;
		}
		return isValid;
	}
	
	@Override
	public void addStatement(Resource subject, URI predicate, Node object)
	        throws ModelRuntimeException {
		assertModel();
		try {
			// convert parameters to OpenRDF data types
			org.openrdf.model.Resource openRdfSubject = (org.openrdf.model.Resource)ConversionUtil
			        .toOpenRDF(subject, this.valueFactory);
			org.openrdf.model.URI openRdfPredicate = ConversionUtil.toOpenRDF(predicate,
			        this.valueFactory);
			Value openRdfObject = ConversionUtil.toOpenRDF(object, this.valueFactory);
			
			// add the statement
			this.connection.add(openRdfSubject, openRdfPredicate, openRdfObject,
			        this.openRdfContext);
			if(log.isDebugEnabled()) {
				this.connection.commit();
				if(!contains(subject, predicate, object)) {
					log.warn("You just added a statement ("
					        + subject
					        + " "
					        + predicate
					        + " "
					        + object
					        + " ) which could not be stored. Most likely cause: http://openrdf.org/issues/browse/SES-521");
				}
				
			}
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	public void removeAll(Iterator<? extends Statement> iterator) throws ModelRuntimeException {
		if(this.isLocked()) {
			throw new ModelRuntimeException("Model is locked, cannot perform an update.");
		}
		// do not auto-commit
		assertModel();
		try {
			this.connection.begin();
			try {
				try {
					// remove all
					while(iterator.hasNext()) {
						org.openrdf.model.Statement s = ConversionUtil.toOpenRDF(iterator.next(),
						        this.valueFactory);
						this.connection.remove(s, this.openRdfContext);
					}
					this.connection.commit();
				} catch(RepositoryException x) {
					this.connection.rollback();
				}
			} finally {
				this.connection.commit();
			}
		} catch(RepositoryException x) {
			throw new ModelRuntimeException(x);
		}
	}
	
	/* for performance reasons */
	@Override
	public void removeAll() throws ModelRuntimeException {
		if(this.isLocked()) {
			throw new ModelRuntimeException("Model is locked, cannot perform an update.");
		}
		// do not auto-commit
		assertModel();
		try {
			this.connection.begin();
			// remove all
			this.connection.clear(this.openRdfContext);
			this.connection.commit();
		} catch(RepositoryException x) {
			throw new ModelRuntimeException(x);
		}
		
	}
	
	@Override
	public void addAll(Iterator<? extends Statement> iterator) throws ModelRuntimeException {
		if(this.isLocked()) {
			throw new ModelRuntimeException("Model is locked, cannot perform an update.");
		}
		// do not auto-commit
		assertModel();
		try {
			this.connection.begin();
			try {
				try {
					// add
					while(iterator.hasNext()) {
						org.openrdf.model.Statement s = ConversionUtil.toOpenRDF(iterator.next(),
						        this.valueFactory);
						this.connection.add(s, this.openRdfContext);
					}
					this.connection.commit();
				} catch(RepositoryException x) {
					this.connection.rollback();
				}
			} finally {
				this.connection.commit();
			}
		} catch(RepositoryException x) {
			throw new ModelRuntimeException(x);
		}
	}
	
	@Override
	public void removeStatement(Resource subject, URI predicate, Node object)
	        throws ModelRuntimeException {
		assertModel();
		try {
			// convert parameters to OpenRDF data types
			org.openrdf.model.Resource openRdfSubject = (org.openrdf.model.Resource)ConversionUtil
			        .toOpenRDF(subject, this.valueFactory);
			org.openrdf.model.URI openRdfPredicate = ConversionUtil.toOpenRDF(predicate,
			        this.valueFactory);
			Value openRdfObject = ConversionUtil.toOpenRDF(object, this.valueFactory);
			
			// remove the statement
			this.connection.remove(openRdfSubject, openRdfPredicate, openRdfObject,
			        this.openRdfContext);
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
			
		}
	}
	
	@Override
	public ClosableIterator<org.ontoware.rdf2go.model.Statement> findStatements(
	        ResourceOrVariable subject, UriOrVariable predicate, NodeOrVariable object)
	        throws ModelRuntimeException {
		assertModel();
		// convert parameters to OpenRDF data types
		org.openrdf.model.Resource openRdfSubject = (org.openrdf.model.Resource)ConversionUtil
		        .toOpenRDF(subject, this.valueFactory);
		org.openrdf.model.URI openRdfPredicate = (org.openrdf.model.URI)ConversionUtil.toOpenRDF(
		        predicate, this.valueFactory);
		Value openRdfObject = ConversionUtil.toOpenRDF(object, this.valueFactory);
		
		try {
			// find the matching statements
			CloseableIteration<? extends org.openrdf.model.Statement,? extends OpenRDFException> statements = this.connection
			        .getStatements(openRdfSubject, openRdfPredicate, openRdfObject, true,
			                this.openRdfContext);
			// wrap them in a StatementIterable
			return new StatementIterator(statements, this);
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	public boolean sparqlAsk(String query) throws ModelRuntimeException {
		assertModel();
		try {
			boolean result = this.connection.prepareBooleanQuery(QueryLanguage.SPARQL, query)
			        .evaluate();
			return result;
		} catch(MalformedQueryException e) {
			throw new ModelRuntimeException(e);
		} catch(UnsupportedQueryLanguageException e) {
			throw new ModelRuntimeException(e);
		} catch(QueryEvaluationException e) {
			throw new ModelRuntimeException(e);
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	public ClosableIterable<Statement> sparqlDescribe(String query) throws ModelRuntimeException {
		assertModel();
		try {
			GraphQueryResult graphQueryResult = this.connection.prepareGraphQuery(
			        QueryLanguage.SPARQL, query).evaluate();
			return new GraphIterable(graphQueryResult, this);
		} catch(MalformedQueryException e) {
			throw new ModelRuntimeException(e);
		} catch(UnsupportedQueryLanguageException e) {
			throw new ModelRuntimeException(e);
		} catch(QueryEvaluationException e) {
			throw new ModelRuntimeException(e);
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	public ClosableIterable<Statement> sparqlConstruct(String query) throws ModelRuntimeException {
		assertModel();
		try {
			GraphQueryResult graphQueryResult = this.connection.prepareGraphQuery(
			        QueryLanguage.SPARQL, query).evaluate();
			return new GraphIterable(graphQueryResult, this);
		} catch(MalformedQueryException e) {
			throw new ModelRuntimeException(e);
		} catch(UnsupportedQueryLanguageException e) {
			throw new ModelRuntimeException(e);
		} catch(QueryEvaluationException e) {
			throw new ModelRuntimeException(e);
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	/* enable SeRQL queries, too */
	@Override
	public QueryResultTable querySelect(String query, String querylanguage)
	        throws QueryLanguageNotSupportedException, ModelRuntimeException {
		assertModel();
		if(querylanguage.equalsIgnoreCase("SPARQL"))
			return sparqlSelect(query);
		else {
			QueryLanguage ql = QueryLanguage.valueOf(querylanguage);
			if(ql == null) {
				throw new QueryLanguageNotSupportedException("Unsupported query language: '"
				        + querylanguage + "'");
			}
			return new RepositoryQueryResultTable(query, ql, this.connection);
		}
	}
	
	/* enable SeRQL queries, too */
	@Override
	public ClosableIterable<Statement> queryConstruct(String query, String querylanguage)
	        throws QueryLanguageNotSupportedException, ModelRuntimeException {
		assertModel();
		if(querylanguage.equalsIgnoreCase("SPARQL"))
			return sparqlConstruct(query);
		else {
			QueryLanguage ql = QueryLanguage.valueOf(querylanguage);
			if(ql == null) {
				throw new QueryLanguageNotSupportedException("Unsupported query language: '"
				        + querylanguage + "'");
			}
			try {
				GraphQueryResult graphQueryResult = this.connection.prepareGraphQuery(ql, query)
				        .evaluate();
				return new GraphIterable(graphQueryResult, this);
			} catch(MalformedQueryException e) {
				throw new ModelRuntimeException(e);
			} catch(UnsupportedQueryLanguageException e) {
				throw new ModelRuntimeException(e);
			} catch(QueryEvaluationException e) {
				throw new ModelRuntimeException(e);
			} catch(RepositoryException e) {
				throw new ModelRuntimeException(e);
			}
		}
	}
	
	@Override
	public QueryResultTable sparqlSelect(String queryString) throws ModelRuntimeException {
		assertModel();
		return new RepositoryQueryResultTable(queryString, this.connection);
	}
	
	@Override
	public ClosableIterator<Statement> iterator() {
		assertModel();
		try {
			CloseableIteration<? extends org.openrdf.model.Statement,RepositoryException> statements = this.connection
			        .getStatements(null, null, null, true, this.openRdfContext);
			return new StatementIterator(statements, this);
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	public Object getUnderlyingModelImplementation() {
		return this.repository;
	}
	
	public void setUnderlyingModelImplementation(Object object) {
		this.repository = (Repository)object;
	}
	
	@Override
	public long size() throws ModelRuntimeException {
		assertModel();
		try {
			return this.connection.size(this.openRdfContext);
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	public URI getContextURI() {
		if(this.context.toString().equals(DEFAULT_CONTEXT)) {
			return null;
		} else {
			return this.context;
		}
	}
	
	@Override
	public synchronized boolean isLocked() {
		return this.locked;
	}
	
	/**
	 * Locking a RepositoryModel disables auto-commit mode and starts a new
	 * transaction, which is left open until this RepositoryModel is unlocked.
	 */
	@Override
	public synchronized void lock() throws LockException {
		if(isLocked()) {
			return;
		}
		
		try {
			// mark this model as locked
			this.locked = true;

			// flush everything that has not been commited yet
			this.connection.commit();
		} catch(RepositoryException e) {
			throw new LockException(e);
		}
	}
	
	/**
	 * Ends the locking status, committing all changed that have been made since
	 * this RepositoryModel was locked and switching back to auto-commit mode.
	 */
	@Override
	public synchronized void unlock() {
		if(!isLocked()) {
			return;
		}
		
		try {
			
			// commit all changes
			this.connection.commit();
			
			// unlock this model
			this.locked = false;
		} catch(RepositoryException e) {
			// TODO: a LockException would be more appropriate IMHO but this
			// requires a change to the RDF2Go API
			throw new ModelRuntimeException(e);
		}
	}
	
	public synchronized void rollback() {
		if(!isLocked()) {
			return;
		}
		
		assertModel();
		try {
			this.connection.rollback();
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	public void dump() {
		assertModel();
		Iterator<Statement> iterator = iterator();
		System.out
		        .println("Dumping Repository contents ----------------------------------------------");
		
		while(iterator.hasNext()) {
			iterator.next().dump(null);
		}
	}
	
	@Override
	public void readFrom(InputStream stream) throws IOException, ModelRuntimeException {
		readFrom(stream, RDFFormat.RDFXML, "");
	}
	
	@Override
	public void readFrom(InputStream stream, Syntax syntax) throws IOException,
	        ModelRuntimeException {
		
		readFrom(stream, getRDFFormat(syntax), "");
	}
	
	@Override
    public void readFrom(InputStream stream, Syntax syntax, String baseURI) throws IOException,
            ModelRuntimeException {

		readFrom(stream, getRDFFormat(syntax), baseURI);
	}
	
	@Override
	public void readFrom(Reader reader, Syntax syntax, String baseURI)
	        throws ModelRuntimeException, IOException {

		readFrom(reader, getRDFFormat(syntax), baseURI);
		
	}
	
	public void readFrom(InputStream stream, RDFFormat format, String baseURI) throws IOException,
	        ModelRuntimeException {
		assertModel();
		try {
			this.connection.add(stream, baseURI, format, this.openRdfContext);
		} catch(RDFParseException e) {
			throw new ModelRuntimeException(e);
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	public void readFrom(Reader reader) throws IOException, ModelRuntimeException {
		readFrom(reader, RDFFormat.RDFXML, "");
	}
	
	@Override
	public void readFrom(Reader reader, Syntax syntax) throws IOException, ModelRuntimeException {
		
		readFrom(reader, getRDFFormat(syntax), "");
	}
	
	public void readFrom(Reader reader, RDFFormat format, String baseURL) throws IOException,
	        ModelRuntimeException {
		assertModel();
		try {
			this.connection.add(reader, baseURL, format, this.openRdfContext);
		} catch(RDFParseException e) {
			IOException ioe = new IOException();
			ioe.initCause(e);
			throw ioe;
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	public void writeTo(OutputStream stream) throws IOException, ModelRuntimeException {
		writeTo(stream, Syntax.RdfXml);
	}
	
	@Override
	public void writeTo(OutputStream stream, Syntax syntax) throws
	// interface allows it
	        IOException, ModelRuntimeException {
		RDFWriter rdfWriter = Rio.createWriter(getRDFFormat(syntax), stream);
		writeTo(rdfWriter);
	}
	
	@Override
	public void writeTo(Writer writer) throws ModelRuntimeException {
		writeTo(writer, Syntax.RdfXml);
	}
	
	@Override
	public void writeTo(Writer writer, Syntax syntax) throws ModelRuntimeException {
		assertModel();
		RDFWriter rdfWriter = Rio.createWriter(getRDFFormat(syntax), writer);
		writeTo(rdfWriter);
	}
	
	/**
	 * Resolves an RDF2Go {@link Syntax} to an OpenRDF {@link RDFFormat}.
	 * 
	 * @param syntax The RDF2Go Syntax to resolve.
	 * @return A RDFFormat for the specified syntax.
	 * @throws SyntaxNotSupportedException When the Syntax could not be resolved to a
	 *             RDFFormat.
	 */
	public static RDFFormat getRDFFormat(Syntax syntax) throws SyntaxNotSupportedException {
	    for (String mimeType : syntax.getMimeTypes()) {
	        RDFFormat format = RDFFormat.forMIMEType(mimeType);
	        if (format != null) {
	            return format;
	        }
	    }
		throw new SyntaxNotSupportedException("This version of Sesame seems to have no "
		        + "support for " + syntax);
	}
	
	public void writeTo(RDFWriter writer) throws ModelRuntimeException {
		assertModel();
		try {
			this.connection.exportStatements(null, null, null, false, writer, this.openRdfContext);
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		} catch(RDFHandlerException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	@Deprecated
	public void commit() {
		try {
			this.connection.commit();
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	@Deprecated
	public void setAutocommit(boolean autocommit) {
		assertModel();
		
		if (autocommit == false) {
			try {
				this.connection.begin();
			} catch (RepositoryException e) {
				throw new RuntimeException(e);
			}
		}
		else {
			try {
				this.connection.commit();
			} catch(RepositoryException e) {
				throw new RuntimeException(e);
			}
		}
	}
	
	/**
	 * Makes sure that the Connection to the wrapped Repository has been closed.
	 */
	@Override
	public void finalize() throws Throwable {
		try {
			if(this.connection.isOpen()) {
				this.logger.warn(this.getClass().getName() + " not closed, closing now.");
				close();
			}
		} finally {
			super.finalize();
		}
	}
	
	@Override
	protected void assertModel() {
		if(this.repository == null) {
			throw new ModelRuntimeException("Repository is null");
		}
		if(this.connection == null) {
			throw new ModelRuntimeException("Connection is null");
		}
	}
	
	@Override
	public boolean isIsomorphicWith(Model other) {
		ClosableIterator<Statement> it = other.iterator();
		Diff diff = this.getDiff(it);
		it.close();
		Iterator<Statement> addIt = diff.getAdded().iterator();
		Iterator<Statement> removeIt = diff.getAdded().iterator();
		boolean result = !addIt.hasNext() && !removeIt.hasNext();
		return result;
	}
	
	@Override
	public synchronized void update(DiffReader diff) throws ModelRuntimeException {
		if(this.isLocked()) {
			throw new ModelRuntimeException("Model is locked, cannot perform an update.");
		}
		// do not auto-commit
		assertModel();
		try {
			this.connection.begin();
			try {
				try {
					// remove
					Iterator<? extends Statement> it = diff.getRemoved().iterator();
					while(it.hasNext()) {
						org.openrdf.model.Statement s = ConversionUtil.toOpenRDF(it.next(),
						        this.valueFactory);
						this.connection.remove(s, this.openRdfContext);
					}
					// add
					it = diff.getAdded().iterator();
					while(it.hasNext()) {
						org.openrdf.model.Statement s = ConversionUtil.toOpenRDF(it.next(),
						        this.valueFactory);
						this.connection.add(s, this.openRdfContext);
					}
					this.connection.commit();
				} catch(RepositoryException x) {
					this.logger.warn("Could not commit, rolling back.", x);
					this.connection.rollback();
				}
			} finally {
				this.connection.commit();
			}
		} catch(RepositoryException x) {
			throw new ModelRuntimeException(x);
		}
		
	}
	
	@Override
	public String getNamespace(String prefix) {
		assertModel();
		try {
			return this.connection.getNamespace(prefix);
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	public Map<String,String> getNamespaces() {
		assertModel();
		Map<String,String> nsMap = new HashMap<String,String>();
		try {
			RepositoryResult<Namespace> openrdfMap = this.connection.getNamespaces();
			openrdfMap.enableDuplicateFilter();
			List<Namespace> openrdfList =  Iterations.asList(openrdfMap);
			for(Namespace openrdfNamespace : openrdfList) {
				nsMap.put(openrdfNamespace.getPrefix(), openrdfNamespace.getName());
			}
			return nsMap;
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	public void removeNamespace(String prefix) {
		assertModel();
		try {
			this.connection.removeNamespace(prefix);
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
	
	@Override
	public void setNamespace(String prefix, String namespaceURI) throws IllegalArgumentException {
		assertModel();
		try {
			this.connection.setNamespace(prefix, namespaceURI);
		} catch(RepositoryException e) {
			throw new ModelRuntimeException(e);
		}
	}
}