package controller; import controller.effectiveoutlierness.Max; import controller.effectiveoutlierness.Average; import controller.effectiveoutlierness.Min; import controller.effectiveoutlierness.Calculation; import gui.settings.Settings; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Observable; import util.Failure; import db.Database; import db.DatabaseAccessException; /** * The class {@code SubspaceController} holds the currently active {@link Subspace} and manages the list of all existing * {@link Subspace}s. */ public class SubspaceController extends Observable { /** * The currently active {@link Subspace} in the UI}. */ private Subspace currentActiveSubspace; /** * The method to calculate the effective outlierness by. */ private Calculation calculateEffectiveOutliernessBy; /** * The {@link Database}, where the {@code Subspace}s are stored. */ private final Database database; /** * Constructs a new {@code SubspaceController}. * * @param database * the {@link Database}, where the {@link Subspace}s are stored, it may not be {@code null}. * @throws DatabaseAccessException * if the read operation failed in {@link Database}. */ public SubspaceController(Database database) throws DatabaseAccessException { if (database == null) { throw new IllegalArgumentException("database is null"); } this.database = database; this.currentActiveSubspace = getAllFeatureSubspace(); this.calculateEffectiveOutliernessBy = this.getAllCalculations()[0]; } /** * Returns a list of all existing {@link Subspace}s in the {@link Database}. The first item is a generated * {@link Subspace} with all existing {@link Feature}s. * * @return The list of {@link Subspace}s. * @throws DatabaseAccessException * if read operation failed in {@link Database}. */ public Subspace[] getSubspaces() throws DatabaseAccessException { Subspace[] subspaces = new Subspace[0]; try { Statement stmt = this.database.getConnection().createStatement(); ResultSet rs = stmt.executeQuery("SELECT Count(DISTINCT Id) FROM Subspaces;"); int count = rs.getInt(1); subspaces = new Subspace[count + 1]; // Add first Subspace, with all existing features subspaces[0] = getAllFeatureSubspace(); for (int i = 1; i <= count; i++) { ArrayList<Integer> currentFeatures = new ArrayList<Integer>(); int subspaceId = i; rs = stmt.executeQuery("SELECT Name, FeatureReference FROM Subspaces WHERE Id=" + i + ";"); // set the fields id and name and add the first feature reference to the list rs.next(); String name = rs.getString(1); currentFeatures.add(rs.getInt(2)); // add all remaining features to the list while (rs.next()) { currentFeatures.add(rs.getInt(2)); } Integer[] featureIds = new Integer[currentFeatures.size()]; currentFeatures.toArray(featureIds); subspaces[i] = new Subspace(this, this.database, subspaceId, buildSubspaceName(name, featureIds), featureIds); } stmt.close(); } catch (SQLException e) { throw new DatabaseAccessException(Failure.READ); } return subspaces; } /** * Returns the currently active {@link Subspace} in the UI. * * @return the active subspace. */ public Subspace getActiveSubspace() { return this.currentActiveSubspace; } /** * Changes the currently active {@link Subspace} in the UI. * * @param subspace * The new {@link Subspace}. * @return The newly activated {@link Subspace}. */ public Subspace setActiveSubspace(Subspace subspace) { if (subspace == null) { throw new IllegalArgumentException("subspace is null"); } this.currentActiveSubspace = subspace; this.calculateEffectiveOutliernessBy.resetMinMax(); informObservers(); return this.currentActiveSubspace; } /** * Returns a list with all possible calculations for the effective outlierness. * * @return the list. */ public Calculation[] getAllCalculations() { Calculation[] calcs = { new Average(), new Max(), new Min() }; return calcs; } /** * Returns the currently active strategy to calculate the effective outlierness by. * * @return the strategy. */ public Calculation getCalculateEffectiveOutliernessBy() { return this.calculateEffectiveOutliernessBy; } /** * Sets the strategy to calculate the effective outlierness by. * * @param strategy * the new strategy. */ public void setCalculateEffectiveOutliernessBy(Calculation strategy) { if (strategy == null) { throw new IllegalArgumentException("the new strategy may not be null"); } strategy.resetMinMax(); this.calculateEffectiveOutliernessBy = strategy; informObservers(); } /** * This method builds a String which represents the subspace. * * The query is build by the features within the subspace and looks like: "(feature1,feature2,...)". * * @param features * a list of features. * @return the built string. * @throws DatabaseAccessException * if the read operation failed in {@link Database}. */ private String buildSubspaceName(String subSpaceName, Integer[] features) throws DatabaseAccessException { // features has to be: "1", "3", "6", .. StringBuilder strB = new StringBuilder(); try { Statement stmt = this.database.getConnection().createStatement(); // Get all required names for the features ResultSet rs = stmt.executeQuery("SELECT Name FROM Features WHERE Id IN " + requiredFeatures(features) + ";"); // iterate all names and build the name for the subspace strB.append(subSpaceName); strB.append(" - ("); while (rs.next()) { strB.append(rs.getString(1)); strB.append(','); } strB.deleteCharAt(strB.lastIndexOf(",")); strB.append(")"); stmt.close(); } catch (SQLException e) { throw new DatabaseAccessException(Failure.READ); } return strB.toString(); } /** * This method builds a String with all required features, to insert into the sql query. * * @param features * a list of features. * @return the built string. */ private String requiredFeatures(Integer[] features) { // SELECT Name FROM Features WHERE Id IN; // requiredFeatures has to be: "1", "3", "6", .. StringBuilder strB = new StringBuilder(); strB.append("("); for (int currentFeature : features) { strB.append(currentFeature); strB.append(','); } strB.deleteCharAt(strB.lastIndexOf(",")); strB.append(")"); return strB.toString(); } /** * The method returns a new {@link Subspace} with all existing features. * * @return the newly created {@link Subspace}. * @throws DatabaseAccessException * if the read operation failed in {@link Database}. */ private Subspace getAllFeatureSubspace() throws DatabaseAccessException { Subspace subspace = new Subspace(this, this.database, 0, "No-Features", new Integer[0]); try { Statement stmt = this.database.getConnection().createStatement(); ResultSet rs = stmt.executeQuery("SELECT Id FROM Features;"); ArrayList<Integer> allExistingFeatures = new ArrayList<Integer>(); while (rs.next()) { allExistingFeatures.add(rs.getInt(1)); } Integer[] featureIds = new Integer[allExistingFeatures.size()]; allExistingFeatures.toArray(featureIds); subspace = new Subspace(this, this.database, 0, Settings.getInstance().getResourceBundle() .getString("allFeatures"), featureIds); stmt.close(); } catch (SQLException e) { throw new DatabaseAccessException(Failure.READ); } return subspace; } /** * Returns the minimum value in effective outlierness. * * @return the min value. */ public float getEOMinValue() { return this.calculateEffectiveOutliernessBy.getMinValue(); } /** * Returns the maximum value in effective outlierness. * * @return the max value. */ public float getEOMaxValue() { return this.calculateEffectiveOutliernessBy.getMaxValue(); } /** * This method updates all registered Observer. */ public void informObservers() { // update observers setChanged(); notifyObservers(); } }