<commit_before>/*
// $Id$
// This software is subject to the terms of the Common Public License
// Agreement, available at the following URL:
// http://www.opensource.org/licenses/cpl.html.
// Copyright (C) 2005-2006 <NAME> and others
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
*/

package mondrian.rolap.aggmatcher;

import mondrian.olap.MondrianProperties;
import mondrian.olap.MondrianDef;
import mondrian.olap.Util;
import mondrian.rolap.RolapAggregator;
import mondrian.rolap.RolapStar;
import mondrian.rolap.sql.SqlQuery;
import mondrian.resource.MondrianResource;

import javax.sql.DataSource;

import org.apache.log4j.Logger;

import java.lang.ref.SoftReference;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.ResultSet;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Types;
import java.sql.SQLException;
import java.util.*;

/**
 * This class is used to scrape a database and store information about its
 * tables and columnIter.
 *
 * <p>The structure of this information is as follows:
 * A database has tables. A table has columnIter. A column has one or more usages.
 * A usage might be a column being used as a foreign key or as part of a
 * measure.
 *
 * <p> Tables are created when calling code requests the set of available
 * tables. This call <code>getTables()</code> causes all tables to be loaded.
 * But a table's columnIter are not loaded until, on a table-by-table basis,
 * a request is made to get the set of columnIter associated with the table.
 * Since, the AggTableManager first attempts table name matches (recognition)
 * most tables do not match, so why load their columnIter.
 * Of course, as a result, there are a host of methods that can throw an
 * {@link SQLException}, rats.
 *
 * @author <NAME>
 * @version $Id$
 */
public class JdbcSchema {
    private static final Logger LOGGER =
        Logger.getLogger(JdbcSchema.class);

    private static final MondrianResource mres = MondrianResource.instance();

    /**
     * Get the Logger.
     */
    public Logger getLogger() {
        return LOGGER;
    }

    public interface Factory {
        JdbcSchema makeDB(DataSource dataSource);
        void clearDB(JdbcSchema db);
        void removeDB(JdbcSchema db);
    }

    private static final Map<DataSource, SoftReference<JdbcSchema>> dbMap =
        new HashMap<DataSource, SoftReference<JdbcSchema>>();

    /**
     * How often between sweeping through the dbMap looking for nulls.
     */
    private static final int SWEEP_COUNT = 10;
    private static int sweepDBCount = 0;

    public static class StdFactory implements Factory {
        StdFactory() {
        }
        public JdbcSchema makeDB(DataSource dataSource) {
            JdbcSchema db = new JdbcSchema(dataSource);
            return db;
        }
        public void clearDB(JdbcSchema db) {
            // NoOp
        }
        public void removeDB(JdbcSchema db) {
            // NoOp
        }
    }

    private static Factory factory;

    private static void makeFactory() {
        if (factory == null) {
            String classname =
                    MondrianProperties.instance().JdbcFactoryClass.get();
            if (classname == null) {
                factory = new StdFactory();
            } else {
                try {
                    Class<?> clz = Class.forName(classname);
                    factory = (Factory) clz.newInstance();
                } catch (ClassNotFoundException ex) {
                    throw mres.BadJdbcFactoryClassName.ex(classname);
                } catch (InstantiationException ex) {
                    throw mres.BadJdbcFactoryInstantiation.ex(classname);
                } catch (IllegalAccessException ex) {
                    throw mres.BadJdbcFactoryAccess.ex(classname);
                }
            }
        }
    }

    /**
     * Creates or retrieves an instance of the JdbcSchema for the given
     * DataSource.
     *
     * @param dataSource DataSource
     * @return instance of the JdbcSchema for the given DataSource
     */
    public static synchronized JdbcSchema makeDB(DataSource dataSource) {
        makeFactory();

        JdbcSchema db = null;
        SoftReference<JdbcSchema> ref = dbMap.get(dataSource);
        if (ref != null) {
            db = ref.get();
        }
        if (db == null) {
            db = factory.makeDB(dataSource);
            dbMap.put(dataSource, new SoftReference<JdbcSchema>(db));
        }

        sweepDB();

        return db;
    }

    /**
     * Clears information in a JdbcSchema associated with a DataSource.
     *
     * @param dataSource DataSource
     */
    public static synchronized void clearDB(DataSource dataSource) {
        makeFactory();

        SoftReference<JdbcSchema> ref = dbMap.get(dataSource);
        if (ref != null) {
            JdbcSchema db = ref.get();
            if (db != null) {
                factory.clearDB(db);
                db.clear();
            } else {
                dbMap.remove(dataSource);
            }
        }
        sweepDB();
    }

    /**
     * Removes a JdbcSchema associated with a DataSource.
     *
     * @param dataSource DataSource
     */
    public static synchronized void removeDB(DataSource dataSource) {
        makeFactory();

        SoftReference<JdbcSchema> ref = dbMap.remove(dataSource);
        if (ref != null) {
            JdbcSchema db = ref.get();
            if (db != null) {
                factory.removeDB(db);
                db.remove();
            }
        }
        sweepDB();
    }

    /**
     * Every SWEEP_COUNT calls to this method, go through all elements of
     * the dbMap removing all that either have null values (null SoftReference)
     * or those with SoftReference with null content.
     */
    private static void sweepDB() {
        if (sweepDBCount > SWEEP_COUNT) {
            Iterator<SoftReference<JdbcSchema>> it = dbMap.values().iterator();
            while (it.hasNext()) {
                SoftReference<JdbcSchema> ref = it.next();
                if ((ref == null) || (ref.get() == null)) {
                    try {
                        it.remove();
                    } catch (Exception ex) {
                        // Should not happen, but might still like to
                        // know that something's funky.<commit_msg>MONDRIAN    The sweepDBCount variable was never incremented, so the    empty SoftReferences were never removed<commit_after>/*
// $Id$
// This software is subject to the terms of the Common Public License
// Agreement, available at the following URL:
// http://www.opensource.org/licenses/cpl.html.
// Copyright (C) 2005-2006 <NAME> and others
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
*/

package mondrian.rolap.aggmatcher;

import mondrian.olap.MondrianProperties;
import mondrian.olap.MondrianDef;
import mondrian.olap.Util;
import mondrian.rolap.RolapAggregator;
import mondrian.rolap.RolapStar;
import mondrian.rolap.sql.SqlQuery;
import mondrian.resource.MondrianResource;

import javax.sql.DataSource;

import org.apache.log4j.Logger;

import java.lang.ref.SoftReference;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.ResultSet;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Types;
import java.sql.SQLException;
import java.util.*;

/**
 * This class is used to scrape a database and store information about its
 * tables and columnIter.
 *
 * <p>The structure of this information is as follows:
 * A database has tables. A table has columnIter. A column has one or more usages.
 * A usage might be a column being used as a foreign key or as part of a
 * measure.
 *
 * <p> Tables are created when calling code requests the set of available
 * tables. This call <code>getTables()</code> causes all tables to be loaded.
 * But a table's columnIter are not loaded until, on a table-by-table basis,
 * a request is made to get the set of columnIter associated with the table.
 * Since, the AggTableManager first attempts table name matches (recognition)
 * most tables do not match, so why load their columnIter.
 * Of course, as a result, there are a host of methods that can throw an
 * {@link SQLException}, rats.
 *
 * @author <NAME>
 * @version $Id$
 */
public class JdbcSchema {
    private static final Logger LOGGER =
        Logger.getLogger(JdbcSchema.class);

    private static final MondrianResource mres = MondrianResource.instance();

    /**
     * Get the Logger.
     */
    public Logger getLogger() {
        return LOGGER;
    }

    public interface Factory {
        JdbcSchema makeDB(DataSource dataSource);
        void clearDB(JdbcSchema db);
        void removeDB(JdbcSchema db);
    }

    private static final Map<DataSource, SoftReference<JdbcSchema>> dbMap =
        new HashMap<DataSource, SoftReference<JdbcSchema>>();

    /**
     * How often between sweeping through the dbMap looking for nulls.
     */
    private static final int SWEEP_COUNT = 10;
    private static int sweepDBCount = 0;

    public static class StdFactory implements Factory {
        StdFactory() {
        }
        public JdbcSchema makeDB(DataSource dataSource) {
            JdbcSchema db = new JdbcSchema(dataSource);
            return db;
        }
        public void clearDB(JdbcSchema db) {
            // NoOp
        }
        public void removeDB(JdbcSchema db) {
            // NoOp
        }
    }

    private static Factory factory;

    private static void makeFactory() {
        if (factory == null) {
            String classname =
                    MondrianProperties.instance().JdbcFactoryClass.get();
            if (classname == null) {
                factory = new StdFactory();
            } else {
                try {
                    Class<?> clz = Class.forName(classname);
                    factory = (Factory) clz.newInstance();
                } catch (ClassNotFoundException ex) {
                    throw mres.BadJdbcFactoryClassName.ex(classname);
                } catch (InstantiationException ex) {
                    throw mres.BadJdbcFactoryInstantiation.ex(classname);
                } catch (IllegalAccessException ex) {
                    throw mres.BadJdbcFactoryAccess.ex(classname);
                }
            }
        }
    }

    /**
     * Creates or retrieves an instance of the JdbcSchema for the given
     * DataSource.
     *
     * @param dataSource DataSource
     * @return instance of the JdbcSchema for the given DataSource
     */
    public static synchronized JdbcSchema makeDB(DataSource dataSource) {
        makeFactory();

        JdbcSchema db = null;
        SoftReference<JdbcSchema> ref = dbMap.get(dataSource);
        if (ref != null) {
            db = ref.get();
        }
        if (db == null) {
            db = factory.makeDB(dataSource);
            dbMap.put(dataSource, new SoftReference<JdbcSchema>(db));
        }

        sweepDB();

        return db;
    }

    /**
     * Clears information in a JdbcSchema associated with a DataSource.
     *
     * @param dataSource DataSource
     */
    public static synchronized void clearDB(DataSource dataSource) {
        makeFactory();

        SoftReference<JdbcSchema> ref = dbMap.get(dataSource);
        if (ref != null) {
            JdbcSchema db = ref.get();
            if (db != null) {
                factory.clearDB(db);
                db.clear();
            } else {
                dbMap.remove(dataSource);
            }
        }
        sweepDB();
    }

    /**
     * Removes a JdbcSchema associated with a DataSource.
     *
     * @param dataSource DataSource
     */
    public static synchronized void removeDB(DataSource dataSource) {
        makeFactory();

        SoftReference<JdbcSchema> ref = dbMap.remove(dataSource);
        if (ref != null) {
            JdbcSchema db = ref.get();
            if (db != null) {
                factory.removeDB(db);
                db.remove();
            }
        }
        sweepDB();
    }

    /**
     * Every SWEEP_COUNT calls to this method, go through all elements of
     * the dbMap removing all that either have null values (null SoftReference)
     * or those with SoftReference with null content.
     */
    private static void sweepDB() {
        if (sweepDBCount++ > SWEEP_COUNT) {
            Iterator<SoftReference<JdbcSchema>> it = dbMap.values().iterator();
            while (it.hasNext()) {
                SoftReference<JdbcSchema> ref = it.next();
                if ((ref == null) || (ref.get() == null)) {
                    try {
                        it.remove();
                    } catch (Exception ex) {
                        // Should not happen, but might still like to
                        // know that something's funky.
{'fraction_non_alphanumeric': 0.09033206904559177, 'fraction_numerical': 0.0018577289263874914, 'mean_word_length': 2.105769230769231, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2885132', 'n_tokens_mistral': 3519, 'n_tokens_neox': 3422, 'n_words': 1108}