org.gdms.source.DefaultSourceManager.java Source code

Java tutorial

Introduction

Here is the source code for org.gdms.source.DefaultSourceManager.java

Source

/**
 * The GDMS library (Generic Datasource Management System)
 * is a middleware dedicated to the management of various kinds of
 * data-sources such as spatial vectorial data or alphanumeric. Based
 * on the JTS library and conform to the OGC simple feature access
 * specifications, it provides a complete and robust API to manipulate
 * in a SQL way remote DBMS (PostgreSQL, H2...) or flat files (.shp,
 * .csv...).
 *
 * Gdms is distributed under GPL 3 license. It is produced by the "Atelier SIG"
 * team of the IRSTV Institute <http://www.irstv.fr/> CNRS FR 2488.
 *
 * Copyright (C) 2007-2012 IRSTV FR CNRS 2488
 *
 * This file is part of Gdms.
 *
 * Gdms is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * Gdms is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * Gdms. If not, see <http://www.gnu.org/licenses/>.
 *
 * For more information, please consult: <http://www.orbisgis.org/>
 *
 * or contact directly:
 * info@orbisgis.org
 */
package org.gdms.source;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.*;
import java.util.regex.Pattern;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.gdms.data.*;
import org.gdms.data.db.DBSource;
import org.gdms.data.db.DBTableSourceDefinition;
import org.gdms.data.exporter.ExportSourceDefinition;
import org.gdms.data.exporter.FileExportDefinition;
import org.gdms.data.file.FileSourceCreation;
import org.gdms.data.file.FileSourceDefinition;
import org.gdms.data.importer.FileImportDefinition;
import org.gdms.data.importer.ImportSourceDefinition;
import org.gdms.data.memory.MemorySourceCreation;
import org.gdms.data.memory.MemorySourceDefinition;
import org.gdms.data.schema.*;
import org.gdms.data.sql.SQLSourceDefinition;
import org.gdms.data.stream.WMSStreamSource;
import org.gdms.data.stream.StreamSourceDefinition;
import org.gdms.driver.DataSet;
import org.gdms.driver.Driver;
import org.gdms.driver.DriverException;
import org.gdms.driver.MemoryDriver;
import org.gdms.driver.asc.AscDriver;
import org.gdms.driver.csv.CSVDriver;
import org.gdms.driver.dbf.DBFDriver;
import org.gdms.driver.driverManager.DriverLoadException;
import org.gdms.driver.driverManager.DriverManager;
import org.gdms.driver.dxf.DXFDriver;
import org.gdms.driver.gdms.GdmsDriver;
import org.gdms.driver.geojson.GeoJsonExporter;
import org.gdms.driver.geojson.GeoJsonImporter;
import org.gdms.driver.geotif.TifDriver;
import org.gdms.driver.hsqldb.HSQLDBDriver;
import org.gdms.driver.jpg.JPGDriver;
import org.gdms.driver.mifmid.MifMidDriver;
import org.gdms.driver.png.PngDriver;
import org.gdms.driver.postgresql.PostgreSQLDriver;
import org.gdms.driver.shapefile.ShapefileDriver;
import org.gdms.driver.wms.SimpleWMSDriver;
import org.gdms.source.directory.Source;
import org.gdms.source.directory.Sources;
import org.gdms.sql.engine.Engine;
import org.gdms.sql.engine.ParseException;
import org.gdms.sql.engine.SQLStatement;
import org.orbisgis.progress.NullProgressMonitor;
import org.orbisgis.progress.ProgressMonitor;

public final class DefaultSourceManager implements SourceManager {

    private static final Logger LOG = Logger.getLogger(DefaultSourceManager.class);
    private Map<String, ExtendedSource> nameSource;
    private Map<String, String> nameMapping;
    private final List<SourceListener> listeners = new ArrayList<SourceListener>();
    private final List<CommitListener> commitListeners = new ArrayList<CommitListener>();
    private DataSourceFactory dsf;
    private DriverManager dm = new DriverManager();
    private String baseDir;
    private Sources sources;
    private JAXBContext jc;
    private Schema schema;
    private String lastUID = "gdms" + System.currentTimeMillis();
    private List<String> contextPaths = new ArrayList<String>();
    private static final Pattern DOT = Pattern.compile("\\.");

    public DefaultSourceManager(DataSourceFactory dsf, String baseDir) {
        dm.registerDriver(CSVDriver.class);
        dm.registerDriver(DBFDriver.class);
        dm.registerDriver(ShapefileDriver.class);
        dm.registerDriver(PostgreSQLDriver.class);
        dm.registerDriver(HSQLDBDriver.class);
        dm.registerDriver(GdmsDriver.class);
        dm.registerDriver(TifDriver.class);
        dm.registerDriver(AscDriver.class);
        dm.registerDriver(JPGDriver.class);
        dm.registerDriver(PngDriver.class);
        dm.registerDriver(SimpleWMSDriver.class);
        dm.registerImporter(DXFDriver.class);
        dm.registerImporter(MifMidDriver.class);
        dm.registerImporter(GeoJsonImporter.class);
        dm.registerExporter(GeoJsonExporter.class);
        this.dsf = dsf;
        this.baseDir = baseDir;
        contextPaths.add("org.gdms.source.directory");
    }

    @Override
    public void init() throws IOException {
        if (nameSource != null && !nameSource.isEmpty()) {
            throw new InitializationException(
                    "Cannot be initialized: " + "there already are sources associated with this SourceManager.");
        }
        changeSourceInfoDirectory(baseDir);
        Iterator<ExtendedSource> it = nameSource.values().iterator();
        while (it.hasNext()) {
            ExtendedSource source = it.next();
            try {
                source.init();
            } catch (DriverException e) {
                LOG.warn("The source could not be created: " + source.getName(), e);
            }
        }
        LOG.trace("Source manager initialized");
    }

    private File getDirectoryFile() {
        return new File(baseDir, "directory.xml");
    }

    @Override
    public void saveStatus() throws DriverException {
        try {
            Sources sourcesToStore = new Sources();
            List<Source> sourceElements = sourcesToStore.getSource();
            // Get the well known sources
            for (Source sourceElement : sources.getSource()) {
                ExtendedSource src = nameSource.get(sourceElement.getName());
                if (src.isSystemTableSource()) {
                    continue;
                }
                if (src.isWellKnownName()) {
                    removeNonWellKnownDependencies(sourceElement);
                    sourceElements.add(sourceElement);
                }
            }
            // Calculate the checksum
            for (Source source : sourceElements) {
                ExtendedSource src = nameSource.get(source.getName());
                if (src.isSystemTableSource() || src.isLiveSource()) {
                    continue;
                }
                source.setChecksum(src.getChecksum());
            }
            createFile(getDirectoryFile());
            FileOutputStream fileOutputStream = null;
            try {
                fileOutputStream = new FileOutputStream(getDirectoryFile());
                jc.createMarshaller().marshal(sourcesToStore, fileOutputStream);
            } finally {
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
            }
        } catch (JAXBException e) {
            throw new DriverException(e);
        } catch (IOException e) {
            throw new DriverException(e);
        }
    }

    private void removeNonWellKnownDependencies(Source sourceElement) {
        removeNonWellKnown(sourceElement.getReferencedSource());
        removeNonWellKnown(sourceElement.getReferencingSource());
    }

    private void removeNonWellKnown(List<String> sourceNameList) {
        ArrayList<String> toRemove = new ArrayList<String>();
        for (String referenced : sourceNameList) {
            if (!getSource(referenced).isWellKnownName()) {
                toRemove.add(referenced);
            }
        }
        for (String depToRemove : toRemove) {
            sourceNameList.remove(depToRemove);
        }
    }

    private void createFile(File file) throws IOException {
        if (!file.exists()) {
            if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {
                throw new IOException("Cannot create dirs: " + file);
            }
            if (!file.createNewFile()) {
                throw new IOException("Cannot create file: " + file);
            }
            PrintWriter pw = new PrintWriter(file);
            pw.println("<sources>");
            pw.println("</sources>");
            pw.close();
        }
    }

    @Override
    public void setDriverManager(DriverManager dm) {
        this.dm = dm;
    }

    @Override
    public DriverManager getDriverManager() {
        return this.dm;
    }

    @Override
    public boolean addSourceListener(SourceListener e) {
        return listeners.add(e);
    }

    @Override
    public boolean removeSourceListener(SourceListener o) {
        return listeners.remove(o);
    }

    @Override
    public void removeAll() throws IOException {
        File f = new File(baseDir);
        File[] files = f.listFiles();
        for (File file : files) {
            if (file.getName().startsWith(".")) {
                continue;
            }
            if (!file.getName().equals("directory.xml") && !file.isDirectory() && !file.delete()) {
                throw new IOException("Cannot delete file associated with property: " + file.getAbsolutePath());

            }
        }
        Iterator<String> it = nameSource.keySet().iterator();
        while (it.hasNext()) {
            String sourceName = it.next();
            fireSourceRemoved(sourceName);
        }
        nameSource.clear();
        nameMapping.clear();
        sources.getSource().clear();

        loadSystemTables();
    }

    @Override
    public boolean remove(String name) {
        return remove(name, false);
    }

    /**
     * Removes a source and delete its physic storage.
     *
     * @param name
     * @param purge
     * @return false if there was no source to remove, true otherwise
     */
    public boolean remove(String name, boolean purge) {
        LOG.trace("Removing source");
        try {
            name = getMainNameFor(name);
        } catch (NoSuchTableException e) {
            return false;
        }
        if (!nameSource.containsKey(name)) {
            return false;
        }

        ExtendedSource toRemove = nameSource.get(name);
        // Check if some source depends on it
        String[] referencingSources = toRemove.getReferencingSources();
        ArrayList<String> notWellKnown = new ArrayList<String>();
        if (referencingSources.length > 0) {
            boolean anyWellKnown = false;
            for (String referencingSource : referencingSources) {
                if (getSource(referencingSource).isWellKnownName()) {
                    anyWellKnown = true;
                } else {
                    notWellKnown.add(referencingSource);
                }
            }
            if (anyWellKnown) {
                StringBuilder msg = new StringBuilder();
                msg.append("The source is used by the following sources: ");
                for (String dep : referencingSources) {
                    msg.append(dep).append(", ");
                }
                String s = msg.substring(0, msg.length() - 2);
                throw new IllegalStateException(s);
            }
        }

        toRemove.removeFromXML();
        nameSource.remove(name);

        removeFromSchema(name);

        for (String nwk : notWellKnown) {
            remove(nwk);
        }

        fireSourceRemoved(name);

        if (purge) {
            try {
                toRemove.getDataSourceDefinition().delete();
            } catch (DriverException ex) {
                throw new IllegalStateException(ex);
            }
        }
        //We have a <File,Driver> association in the DriverManager, we must get rid of it.
        File assoc = toRemove.getFile();
        if (assoc != null) {
            dm.removeFile(assoc);
        }

        return true;
    }

    @Override
    public boolean delete(String name) {
        return remove(name, true);
    }

    private void fireSourceRemoved(String name) {
        List<String> names = getNamesFor(name);
        List<String> namesToRemove = names;
        for (String nameToRemove : namesToRemove) {
            nameMapping.remove(nameToRemove);
        }

        fireSourceRemoved(name, names);
    }

    private void fireSourceRemoved(String name, List<String> names) {
        SourceListener[] list = listeners.toArray(new SourceListener[listeners.size()]);
        for (SourceListener listener : list) {
            listener.sourceRemoved(
                    new SourceRemovalEvent(name, names.toArray(new String[names.size()]), true, this));
        }
    }

    private List<String> getNamesFor(String dsName) {
        Iterator<String> names = nameMapping.keySet().iterator();
        ArrayList<String> namesToChange = new ArrayList<String>();
        while (names.hasNext()) {
            String name = names.next();
            if (nameMapping.get(name).equals(dsName)) {
                namesToChange.add(name);
            }
        }
        return namesToChange;
    }

    private ExtendedSource getExtendedSource(String name) {
        try {
            name = getMainNameFor(name);
            return nameSource.get(name);
        } catch (NoSuchTableException e) {
            return null;
        }
    }

    @Override
    public org.gdms.source.Source getSource(String name) {
        return getExtendedSource(name);
    }

    @Override
    public void register(String name, URI uri) {
        registerURI(name, true, uri);
    }

    private void registerURI(String name, boolean wellKnown, URI uri) {
        final String scheme = uri.getScheme().toLowerCase();
        if ("file".equals(scheme)) {
            if (wellKnown) {
                register(name, new FileSourceCreation(new File(uri), null));
            } else {
                register(name, false,
                        new FileSourceDefinition(new File(uri), DriverManager.DEFAULT_SINGLE_TABLE_NAME));
            }
        } else if (scheme.startsWith("http")) {
            try {
                WMSStreamSource source = new WMSStreamSource(uri);
                register(name, wellKnown, new StreamSourceDefinition(source));
            } catch (UnsupportedEncodingException ex) {
                LOG.error("Fail to register source " + uri.toString(), ex);
            }
        } else {
            String table = null;
            String sch = null;
            String user = null;
            String password = null;
            boolean ssl = false;
            String dbName = uri.getPath();
            if (dbName.startsWith("/")) {
                dbName = dbName.substring(1);
            }
            String[] params = uri.getQuery().split("&");
            for (int i = 0; i < params.length; i++) {
                String[] vals = params[i].split("=");
                if ("table".equalsIgnoreCase(vals[0])) {
                    table = vals[1];
                } else if ("schema".equalsIgnoreCase(vals[0])) {
                    sch = vals[1];
                } else if ("user".equalsIgnoreCase(vals[0])) {
                    user = vals[1];
                } else if ("password".equalsIgnoreCase(vals[0])) {
                    password = vals[1];
                } else if ("ssl".equalsIgnoreCase(vals[0]) && "true".equalsIgnoreCase(vals[1])) {
                    ssl = true;
                }
            }
            DBSource s = new DBSource(uri.getHost(), uri.getPort(), dbName, user, password, sch, table,
                    "jdbc:" + scheme, ssl);
            register(name, wellKnown, new DBTableSourceDefinition(s));
        }
    }

    @Override
    public void register(String name, File file) {
        register(name, new FileSourceCreation(file, null));
    }

    @Override
    public void register(String name, String sql) throws ParseException {
        SQLStatement s = Engine.parse(sql, dsf.getProperties());
        register(name, new SQLSourceDefinition(s));
    }

    @Override
    public void register(String name, DBSource dbTable) {
        register(name, new DBTableSourceDefinition(dbTable));
    }

    @Override
    public void register(String name, WMSStreamSource streamSource) {
        register(name, new StreamSourceDefinition(streamSource));
    }

    @Override
    public void register(String name, MemoryDriver driver) {
        register(name, new MemorySourceCreation(driver));
    }

    @Override
    public void register(String name, DataSourceCreation cr) {
        try {
            cr.setDataSourceFactory(dsf);
            String[] t = cr.getAvailableTables();
            if (t.length > 1) {
                for (int i = 0; i < t.length; i++) {
                    register(name + "." + t[i], cr.create(t[i]));
                }
            } else if (t.length == 1) {
                register(name, cr.create(t[0]));
            } else {
                throw new IllegalStateException("Driver for " + name + " declares no tables!");
            }
        } catch (DriverException ex) {
            LOG.error("Could not list tables for " + name, ex);
        }
    }

    @Override
    public void register(String name, DataSourceDefinition def) {
        register(name, true, def);
    }

    private void register(String name, boolean wellKnownName, DataSourceDefinition dsd) {
        register(name, wellKnownName, dsd, null);
    }

    private void register(String name, boolean isWellKnownName, DataSourceDefinition dsd, DataSource ds) {

        dsd.setDataSourceFactory(dsf);
        String sourceName = getSourceName(dsd);
        if (sourceName != null) {
            throw new SourceAlreadyExistsException("The source already exists with the name: " + sourceName);
        } else {
            ExtendedSource src = new ExtendedSource(dsf, sources, name, isWellKnownName, baseDir, ds, dsd);
            register(name, src);
        }
    }

    private void register(String name, ExtendedSource src) {
        if (exists(name)) {
            throw new SourceAlreadyExistsException(name);
        }
        LOG.trace("Registering source " + name);
        nameSource.put(name, src);
        try {
            addToSchema(name, src);

        } catch (DriverLoadException ex) {
            LOG.error("Failed to add " + name + " to Gdms schema.", ex);
        } catch (DriverException ex) {
            LOG.error("Failed to add " + name + " to Gdms schema.", ex);
        }
        fireSourceAdded(name, src.isWellKnownName());
    }

    private void addToSchema(String name, ExtendedSource src) throws DriverException {
        if (name.isEmpty()) {
            throw new IllegalArgumentException("Empty table name!");
        }

        // split on the dots '.' into
        // schema1.schema2.schema3.table1

        String[] l = DOT.split(name);
        final DataSourceDefinition dsd = src.getDataSourceDefinition();

        if (l.length == 0) {
            // just a table, we add it to the root schema
            String table = dsd.getDriverTableName();
            schema.addTable(name, dsd.getSchema().getTableByName(table));
        } else {
            Schema s = schema;
            // we get down (possibly creating the schemas on the way)
            // to the last schema before the table
            for (int i = 0; i < l.length - 1; i++) {
                Schema n = s.getSubSchemaByName(l[i]);
                if (n == null) {
                    n = new DefaultSchema(l[i]);
                    s.addSubSchema(n);
                }
                s = n;
            }
            s.addTable(l[l.length - 1], dsd.getSchema().getTableByName(l[l.length - 1]));
        }
    }

    private void removeFromSchema(String name) {
        if (name.isEmpty()) {
            throw new IllegalArgumentException("Empty table name!");
        }

        // split on the dots '.' into
        // schema1.schema2.schema3.table1

        String[] l = DOT.split(name);

        if (l.length <= 1) {
            // just a table, we remove it from the root schema
            schema.removeTable(name);
        } else {
            Deque<Schema> path = new ArrayDeque<Schema>();
            path.add(schema);
            // we get down
            // to the last schema before the table
            for (int i = 0; i < l.length - 1; i++) {
                final Schema n = path.getFirst().getSubSchemaByName(l[i]);
                path.addFirst(n);
            }

            boolean stop = false;
            while (!path.isEmpty() && !stop) {
                // take the last schema in the path (top of the pile)
                final Schema n = path.pollFirst();
                n.removeTable(l[l.length - 1]);
                if (n.getTableCount() != 0 || n.getSubSchemaNames().length != 0) {
                    // the schema is still needed, we must not remove it
                    stop = true;
                } else {
                    Schema p = n.getParentSchema();
                    if (p != null) {
                        p.removeSubSchema(n.getName());
                    } else {
                        // we have reached root, it stays were it is...
                        stop = true;
                    }
                }
            }
        }
    }

    private void fireSourceAdded(String name, boolean wellKnownName) {
        SourceListener[] list = listeners.toArray(new SourceListener[listeners.size()]);
        for (SourceListener listener : list) {
            listener.sourceAdded(new SourceEvent(name, wellKnownName, this));
        }
    }

    @Override
    public String getUID() {
        String name = "gdms" + System.currentTimeMillis();

        while (name.equals(lastUID)) {
            name = "gdms" + System.currentTimeMillis();
        }

        lastUID = name;
        return name;
    }

    @Override
    public String getUniqueName(String base) {
        String tmpName = base;
        int i = 0;
        while (exists(tmpName)) {
            tmpName = base + "_" + i;
            i++;
        }

        return tmpName;
    }

    @Override
    public String nameAndRegister(URI uri) {
        String name = getUID();
        registerURI(name, false, uri);
        return name;
    }

    @Override
    public String nameAndRegister(File file) {
        String name = getUID();
        register(name, false, new FileSourceDefinition(file, DriverManager.DEFAULT_SINGLE_TABLE_NAME));
        return name;
    }

    @Override
    public String nameAndRegister(String sql) throws ParseException {
        SQLStatement s = Engine.parse(sql, dsf.getProperties());
        return nameAndRegister(new SQLSourceDefinition(s));
    }

    @Override
    public String nameAndRegister(DBSource dbTable) {
        String name = getUID();
        register(name, false, new DBTableSourceDefinition(dbTable));
        return name;
    }

    @Override
    public String nameAndRegister(WMSStreamSource streamSource) {
        String name = getUID();
        register(name, false, new StreamSourceDefinition(streamSource));
        return name;
    }

    @Override
    public String nameAndRegister(MemoryDriver driver, String tableName) {
        String name = getUID();
        register(name, false, new MemorySourceDefinition(driver, tableName));
        return name;
    }

    @Override
    public String nameAndRegister(DataSourceDefinition def) {
        String name = getSourceName(def);
        if (name != null) {
            return name;
        } else {
            name = getUID();
            register(name, false, def);
            return name;
        }
    }

    @Override
    public String nameAndRegister(DataSourceCreation dsc) {
        String name = getUID();
        register(name, dsc);
        return name;
    }

    @Override
    public String getSourceName(DataSourceDefinition dsd) {
        Iterator<String> it = nameSource.keySet().iterator();
        while (it.hasNext()) {
            String name = it.next();
            ExtendedSource src = nameSource.get(name);
            if (src.getDataSourceDefinition().equals(dsd)) {
                return name;
            }
        }

        return null;
    }

    public DataSource getDataSource(String name, ProgressMonitor pm)
            throws NoSuchTableException, DataSourceCreationException {

        name = getMainNameFor(name);

        ExtendedSource src = nameSource.get(name);

        DataSource dataSource = src.getDataSource();
        if (dataSource == null) {
            DataSourceDefinition dsd = src.getDataSourceDefinition();

            if (dsd == null) {
                throw new NoSuchTableException(name);
            } else {
                DataSource ds = dsd.createDataSource(name, pm);
                if (pm.isCancelled()) {
                    dataSource = null;
                } else {
                    ds = new OCCounterDecorator(ds);
                    ds.setDataSourceFactory(dsf);
                    try {
                        if ((dsd.getType() & SourceManager.LIVE) == 0) {
                            src.setDatasource(ds);
                        }
                    } catch (DriverException ex) {
                        throw new DataSourceCreationException(ex);
                    }
                    dataSource = ds;
                }
            }
        }

        return dataSource;
    }

    @Override
    public void addName(String dsName, String newName) throws NoSuchTableException {
        if (!exists(dsName)) {
            throw new NoSuchTableException(dsName);
        }
        if (exists(newName)) {
            throw new SourceAlreadyExistsException(newName);
        }
        nameMapping.put(newName, dsName);
    }

    @Override
    public void rename(String dsName, String newName) {
        if (nameSource.containsKey(dsName)) {
            if (exists(newName)) {
                throw new SourceAlreadyExistsException(newName);
            }
            ExtendedSource value = nameSource.remove(dsName);
            nameSource.put(newName, value);
            value.setName(newName);

            // update schema
            removeFromSchema(dsName);
            try {
                addToSchema(newName, value);
            } catch (DriverException ex) {
                LOG.error("Failed to add " + newName + " to Gdms schema.", ex);
            }
            changeNameMapping(dsName, newName);

        }

        if (nameMapping.containsKey(dsName)) {
            if (exists(newName)) {
                throw new SourceAlreadyExistsException("The source already exists: " + newName);
            }
            String ds = nameMapping.remove(dsName);
            nameMapping.put(newName, ds);
        }

        fireNameChanged(dsName, newName);
    }

    private void fireNameChanged(String dsName, String newName) {
        SourceListener[] list = listeners.toArray(new SourceListener[listeners.size()]);
        for (SourceListener listener : list) {
            listener.sourceNameChanged(new SourceEvent(dsName, true, this, newName));
        }

    }

    private void changeNameMapping(String dsName, String newName) {
        List<String> namesToChange = getNamesFor(dsName);
        for (int i = 0; i < namesToChange.size(); i++) {
            nameMapping.put(namesToChange.get(i), newName);
        }
    }

    @Override
    public boolean exists(String sourceName) {
        return nameSource.containsKey(sourceName) || nameMapping.containsKey(sourceName);
    }

    @Override
    public String getMainNameFor(String dsName) throws NoSuchTableException {
        if (dsName.startsWith("PUBLIC.")) {
            dsName = dsName.substring(7);
        }
        if (nameMapping.containsKey(dsName)) {
            return nameMapping.get(dsName);
        } else if (nameSource.containsKey(dsName)) {
            return dsName;
        } else {
            throw new NoSuchTableException(dsName);
        }
    }

    @Override
    public void shutdown() throws DataSourceFinalizationException {
        LOG.trace("Shutdown");
        for (String name : nameSource.keySet()) {
            DataSourceDefinition dataSourceDefinition = nameSource.get(name).getDataSourceDefinition();
            if (dataSourceDefinition != null) {
                dataSourceDefinition.freeResources(name);
            }
        }
        nameSource.clear();
    }

    @Override
    public boolean isEmpty() {
        return nameSource.isEmpty() && nameMapping.isEmpty();
    }

    @Override
    public boolean isEmpty(boolean ignoreSystem) {
        if (!ignoreSystem) {
            return isEmpty();
        } else {
            for (ExtendedSource s : nameSource.values()) {
                if (!s.isSystemTableSource()) {
                    return false;
                }
            }
            return true;
        }
    }

    @Override
    public DataSourceDefinition createDataSource(DataSourceCreation dsc, String tableName) throws DriverException {
        LOG.trace("Creating datasource");
        dsc.setDataSourceFactory(dsf);
        return dsc.create(tableName);
    }

    @Override
    public DataSourceDefinition createDataSource(DataSourceCreation dsc) throws DriverException {
        return createDataSource(dsc, DriverManager.DEFAULT_SINGLE_TABLE_NAME);
    }

    public void saveContents(String sourceName, DataSet contents, ProgressMonitor pm) throws DriverException {
        LOG.trace("Saving source to " + sourceName);
        ExtendedSource extendedSource = getExtendedSource(sourceName);
        if (extendedSource == null) {
            throw new IllegalArgumentException("There is no source with the specified name: " + sourceName);
        } else {
            DataSourceDefinition dsd = extendedSource.getDataSourceDefinition();
            dsd.createDataSource(contents, pm);
        }
    }

    @Override
    public File getSourceInfoDirectory() {
        return new File(baseDir);
    }

    @Override
    public void setSourceInfoDirectory(String newDir) throws DriverException {
        saveStatus();
        File newDirectory = new File(newDir);
        if (!newDirectory.exists()) {
            newDirectory.mkdirs();
        }
        File[] childs = getSourceInfoDirectory().listFiles();
        for (File file : childs) {
            if (file.getName().startsWith(".")) {
                continue;
            }
            try {
                FileUtils.copyFile(file, new File(newDirectory, file.getName()));
            } catch (IOException e) {
                throw new DriverException(e);
            }
        }
        this.baseDir = newDir;
    }

    @Override
    public String getMemento() throws IOException {
        StringBuilder ret = new StringBuilder();
        Iterator<String> it = nameSource.keySet().iterator();
        while (it.hasNext()) {
            String sourceName = it.next();
            org.gdms.source.Source source = nameSource.get(sourceName);
            ret.append(sourceName).append("(");
            List<String> aliases = getNamesFor(sourceName);
            for (String alias : aliases) {
                ret.append("-").append(alias).append("-");
            }
            String[] stringPropertyNames = source.getStringPropertyNames();
            for (String propertyName : stringPropertyNames) {
                ret.append("(").append(propertyName).append(",").append(source.getProperty(propertyName))
                        .append(")");
            }
            String[] filePropertyNames = source.getFilePropertyNames();
            for (String propertyName : filePropertyNames) {
                ret.append("(").append(propertyName).append(",")
                        .append(source.getFilePropertyContentsAsString(propertyName)).append(")");
            }
            ret.append(")");
        }

        return ret.toString();
    }

    @Override
    public void removeName(String secondName) {
        if (nameMapping.containsKey(secondName)) {
            nameMapping.remove(secondName);
        }
    }

    @Override
    public void changeSourceInfoDirectory(String newSourceInfoDir) throws IOException {
        this.baseDir = newSourceInfoDir;
        nameSource = new HashMap<String, ExtendedSource>();
        nameMapping = new HashMap<String, String>();

        schema = new RootSchema();

        File file = getDirectoryFile();
        createFile(file);
        StringBuilder b = new StringBuilder();
        b.append(contextPaths.get(0));
        for (int i = 1; i < contextPaths.size(); i++) {
            b.append(':').append(contextPaths.get(i));
        }
        try {
            jc = JAXBContext.newInstance(b.toString(), this.getClass().getClassLoader());
            sources = (Sources) jc.createUnmarshaller().unmarshal(file);

            List<Source> source = sources.getSource();
            for (Source xmlSrc : source) {
                String name = xmlSrc.getName();
                ExtendedSource newSource = new ExtendedSource(dsf, sources, name, true, baseDir, null, null);

                register(name, newSource);
            }
        } catch (JAXBException e) {
            throw new InitializationException(e);
        }

        loadSystemTables();
    }

    /**
     * A method used to register some table system TODO : Must be change with
     * GDMS 2.0
     */
    @Override
    public void loadSystemTables() {

    }

    @Override
    public String[] getSourceNames() {
        return nameSource.keySet().toArray(new String[nameSource.size()]);
    }

    @Override
    public void addCommitListener(CommitListener listener) {
        commitListeners.add(listener);
    }

    @Override
    public void removeCommitListener(CommitListener listener) {
        commitListeners.remove(listener);
    }

    @Override
    public void fireIsCommiting(String name, Object source) throws DriverException {
        CommitListener[] listenerCopy = commitListeners.toArray(new CommitListener[commitListeners.size()]);
        for (CommitListener listener : listenerCopy) {
            if (listener.getName().equals(name)) {
                listener.isCommiting(name, source);
            }
        }
    }

    @Override
    public void fireCommitDone(String name) {
        CommitListener[] listenerCopy = commitListeners.toArray(new CommitListener[commitListeners.size()]);
        for (CommitListener listener : listenerCopy) {
            if (listener.getName().equals(name)) {
                try {
                    listener.commitDone(name);
                } catch (DriverException e) {
                    LOG.error("Cannot refresh commit listener: " + name, e);
                }
            }
        }
        ExtendedSource src = getExtendedSource(name);
        src.getDataSourceDefinition().refresh();
        String[] referencing = src.getReferencingSources();
        for (String referencingSource : referencing) {
            fireCommitDone(referencingSource);
        }
    }

    @Override
    public String[] getAllNames(String sourceName) throws NoSuchTableException {
        final List<String> namesFor = getNamesFor(getMainNameFor(sourceName));
        return namesFor.toArray(new String[namesFor.size()]);
    }

    @Override
    public void addSourceContextPath(String path) {
        if (path == null) {
            throw new IllegalArgumentException("path cannot be null");
        }
        contextPaths.add(path);
    }

    @Override
    public boolean removeSourceContextPath(String path) {
        return contextPaths.remove(path);
    }

    @Override
    public boolean containsSourceContextPath(String path) {
        return contextPaths.contains(path);
    }

    @Override
    public Schema getSchema() {
        return schema;
    }

    @Override
    public boolean removeSchema(String schemaName, boolean purge) {
        if (schemaName.isEmpty()) {
            throw new IllegalArgumentException("Entpy schema name!");
        }

        String[] s = DOT.split(schemaName);
        int i = 0;

        // jump over root schema name
        if (s[i].equals(schema.getName())) {
            i++;
        }

        Schema ss = schema;

        // find subs-schemas
        while (i < s.length) {
            ss = ss.getSubSchemaByName(s[i]);
            if (ss == null) {
                return false;
            }
            i++;
        }

        return removeSchema(ss, purge);
    }

    private boolean removeSchema(Schema s, boolean purge) {
        boolean done = false;

        // remove all sub-schemas
        String[] sub = s.getSubSchemaNames();
        for (int i = 0; i < sub.length; i++) {
            done |= removeSchema(s.getSubSchemaByName(sub[i]), purge);
        }

        // remove all tables
        String[] t = s.getTableNames();
        String fullname = s.getFullyQualifiedName();
        for (int j = 0; j < t.length; j++) {
            done |= remove(fullname + "." + t[j], purge);
        }

        // disconnect from parent
        // parent can be null iff
        // - is the root schema
        // - the remove() above disconnected the last table of the schema (no empty schema is ever kept)
        Schema parent = s.getParentSchema();
        if (parent != null) {
            parent.removeSubSchema(s.getName());
        }

        return done;
    }

    @Override
    public boolean schemaExists(String name) {
        if (name.isEmpty()) {
            throw new IllegalArgumentException("Empty schema name!");
        }

        String[] s = DOT.split(name);
        int i = 0;

        // jump over root schema name
        if (s[i].equals(schema.getName())) {
            i++;
        }

        Schema ss = schema;

        // find subs-schemas
        while (i < s.length) {
            ss = ss.getSubSchemaByName(s[i]);
            if (ss == null) {
                return false;
            }
            i++;
        }

        return true;
    }

    @Override
    public void importFrom(String name, File file) throws DriverException {
        importFrom(name, new FileImportDefinition(file));
    }

    @Override
    public void importFrom(String name, ImportSourceDefinition def) throws DriverException {
        def.setDataSourceFactory(dsf);
        String[] tableNames = def.getSchema().getTableNames();

        if (tableNames.length == 1) {
            DataSourceDefinition dsd = def.importSource(DriverManager.DEFAULT_SINGLE_TABLE_NAME);
            register(name, dsd);
        } else {
            DataSourceDefinition[] dsds = def.importAllSources();

            for (int i = 0; i < dsds.length; i++) {
                register(name + "." + tableNames[i], dsds[i]);
            }
        }
    }

    @Override
    public void exportTo(String name, File file)
            throws DriverException, NoSuchTableException, DataSourceCreationException {
        exportTo(name, new FileExportDefinition(file));
    }

    @Override
    public void exportTo(String name, ExportSourceDefinition def)
            throws DriverException, NoSuchTableException, DataSourceCreationException {
        // differentiate between schema names and table names
        if (exists(name)) {
            DataSource d = getDataSource(name, new NullProgressMonitor());
            d.open();
            exportTo(d, def);
            d.close();
        } else if (schemaExists(name)) {
            String[] names = getSourceNames();
            for (int i = 0; i < names.length; i++) {
                if (names[i].startsWith(name + ".")) {
                    // table is in the schema
                    DataSource d = getDataSource(names[i], new NullProgressMonitor());

                    def.setDataSourceFactory(dsf);
                    String intName = names[i].substring(names[i].lastIndexOf('.') + 1);
                    d.open();
                    if (def.getSchema().getTableCount() != 0) {
                        final Metadata met = def.getSchema().getTableByName(intName);

                        // checks the table is allowed
                        if (met == null) {
                            throw new DriverException("This export definition does not expect" + " a table named '"
                                    + intName + "'. Expected tables are: "
                                    + Arrays.toString(def.getSchema().getTableNames()));
                        }

                        // checks metadata are compatible
                        String error = MetadataUtilities.check(met, d.getMetadata());
                        if (error != null) {
                            throw new DriverException("Cannot export '" + names[i] + "': " + error);
                        }
                    }
                    def.export(d, intName);
                    d.close();
                }
            }
        } else {
            throw new NoSuchTableException(name);
        }
    }

    @Override
    public void exportTo(DataSet dataSet, File file) throws DriverException {
        exportTo(dataSet, new FileExportDefinition(file));
    }

    /**
     * Export a datasource to a file
     * @param dataSource
     * @param def
     * @throws DriverException
     * @throws NoSuchTableException
     * @throws DataSourceCreationException 
     */
    private void exportTo(DataSet dataSet, ExportSourceDefinition def) throws DriverException {
        def.setDataSourceFactory(dsf);
        if (def.getSchema().getTableCount() > 1) {
            throw new DriverException("This export definition expects a schema with "
                    + def.getSchema().getTableCount() + " table, not a single table.");
        } else if (def.getSchema().getTableCount() == 1) {
            final Metadata met = def.getSchema().getTableByName(DriverManager.DEFAULT_SINGLE_TABLE_NAME);
            // checks metadata are compatible
            String error = MetadataUtilities.check(met, dataSet.getMetadata());
            if (error != null) {
                throw new DriverException("Cannot export the datasource': " + error);
            }
        }
        def.export(dataSet, DriverManager.DEFAULT_SINGLE_TABLE_NAME);
    }

    @Override
    public boolean exists(URI uri) {
        return getSourceName(getDataSourceDefinition(uri)) != null;
    }

    /**
     * Return the DBSource constructed from the provided URI
     * @param uri URI with 
     * @return 
     */
    private DBTableSourceDefinition uriToDBDef(URI uri) {
        final String scheme = uri.getScheme().toLowerCase();
        String table = null;
        String sch = null;
        String user = null;
        String password = null;
        boolean ssl = false;
        String dbName = uri.getPath();
        if (dbName.startsWith("/")) {
            dbName = dbName.substring(1);
        }
        String[] params = uri.getQuery().split("&");
        for (int i = 0; i < params.length; i++) {
            String[] vals = params[i].split("=");
            if ("table".equalsIgnoreCase(vals[0])) {
                table = vals[1];
            } else if ("schema".equalsIgnoreCase(vals[0])) {
                sch = vals[1];
            } else if ("user".equalsIgnoreCase(vals[0])) {
                user = vals[1];
            } else if ("password".equalsIgnoreCase(vals[0])) {
                password = vals[1];
            } else if ("ssl".equalsIgnoreCase(vals[0]) && "true".equalsIgnoreCase(vals[1])) {
                ssl = true;
            }
        }
        DBSource s = new DBSource(uri.getHost(), uri.getPort(), dbName, user, password, sch, table,
                "jdbc:" + scheme, ssl);
        return new DBTableSourceDefinition(s);
    }

    private DataSourceDefinition<? extends Driver> getDataSourceDefinition(URI uri)
            throws UnsupportedOperationException {
        final String scheme = uri.getScheme().toLowerCase();
        if ("file".equals(scheme)) {
            return new FileSourceDefinition(new File(uri), DriverManager.DEFAULT_SINGLE_TABLE_NAME);
        } else if (scheme.startsWith("http")) {
            try {
                WMSStreamSource source = new WMSStreamSource(uri);
                return new StreamSourceDefinition(source);
            } catch (UnsupportedEncodingException ex) {
                throw new UnsupportedOperationException("Fail to register source " + uri.toString(), ex);
            }
        } else {
            return uriToDBDef(uri);
        }
    }

    @Override
    public String getNameFor(URI uri) throws NoSuchTableException {
        String sourceName = getSourceName(getDataSourceDefinition(uri));
        if (sourceName == null) {
            throw new NoSuchTableException("for '" + uri.toString() + "'");
        } else {
            return sourceName;
        }
    }
}