/* * Copyright (c) 2011 Patrick Meyer * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package com.itemanalysis.jmetrik.model; import com.itemanalysis.jmetrik.dao.DatabaseAccessObject; import com.itemanalysis.jmetrik.sql.DatabaseName; import com.itemanalysis.jmetrik.sql.VariableTableName; import com.itemanalysis.jmetrik.workspace.*; import com.itemanalysis.psychometrics.data.DataType; import com.itemanalysis.psychometrics.data.ItemType; import com.itemanalysis.psychometrics.data.VariableAttributes; import com.itemanalysis.psychometrics.data.VariableType; import org.apache.log4j.Logger; import javax.swing.*; import javax.swing.table.AbstractTableModel; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class VariableModel extends AbstractTableModel { private String[] columnNames = {"Variable", "Type", "Scoring", "Group", "Label", "Omit", "Not Reached"}; private ArrayList<VariableAttributes> variables = new ArrayList<VariableAttributes>(); private int numCols=columnNames.length,numRows=0; private int dataModified=0; private Connection conn = null; private DatabaseName dbName = null; private VariableTableName tableName = null; static Logger logger = Logger.getLogger("jmetrik-logger"); private DatabaseAccessObject dao = null; private ArrayList<PropertyChangeListener> propertyChangeListeners = null; private ArrayList<VariableChangeListener> variableChangeListeners = null; private ArrayList<DatabaseSelectionListener> databaseChangeListeners = null; //use thread pool to manage data loading private ThreadPoolExecutor threadPool = null; private int threadPoolSize = 1; private int threadPoolSizeMax = 1; private int maxQueueSize = 50; private long threadKeepAliveTime = 10; private final ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(maxQueueSize); // Columns in db // "VARIABLE VARCHAR(30)," + //name (1) // "VARGROUP VARCHAR(30)," + //group (2) // "SCORING VARCHAR(30)," + //scoring (3) // "ITEMTYPE VARCHAR(30)," + //item type (4) // "DATATYPE VARCHAR(30)," + //data type (5) // "LABEL VARCHAR(50))"; //label (6) public VariableModel(Connection conn, DatabaseName dbName, VariableTableName tableName, DatabaseAccessObject dao, ArrayList<PropertyChangeListener> propertyChangeListeners){ this.conn = conn; this.dbName = dbName; this.tableName=tableName; this.dao = dao; this.propertyChangeListeners = propertyChangeListeners; this.variableChangeListeners = new ArrayList<VariableChangeListener>(); this.databaseChangeListeners = new ArrayList<DatabaseSelectionListener>(); threadPool = new ThreadPoolExecutor(threadPoolSize, threadPoolSizeMax, threadKeepAliveTime, TimeUnit.SECONDS, queue); threadPool.prestartCoreThread(); loadData(); } private void loadData(){ Runnable task = new SwingWorker<ArrayList<VariableAttributes>, Void>(){ //connect to db and populate newData protected ArrayList<VariableAttributes> doInBackground()throws Exception{ return dao.getAllVariables(conn, tableName); } protected void done(){ try{ variables = get(); numRows = variables.size(); fireTableDataChanged(); }catch(Exception ex){ logger.fatal(ex.getMessage(), ex); this.firePropertyChange("message", "", "Error - Check log for details."); } } }; threadPool.execute(task); } private void updateTestItemOrder(){ int order = 0; for(VariableAttributes v : variables){ VariableType vType = v.getType(); if(vType.getItemType()== ItemType.BINARY_ITEM || vType.getItemType()==ItemType.POLYTOMOUS_ITEM || vType.getItemType()==ItemType.CONTINUOUS_ITEM){ order++; v.setTestItemOrder(order); } } } public String getTableName(){ return tableName.getTableName(); } public void saveData(){ if(dataModified>0){ try{ Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); ResultSet rs = stmt.executeQuery("SELECT * FROM " + tableName.getNameForDatabase()); for(int i=0;i<variables.size();i++){ rs.absolute(i+1); rs.updateObject(1, variables.get(i).getName().toString()); //name rs.updateObject(2, variables.get(i).getItemGroup()); //group (i.e. subscale) rs.updateObject(3, variables.get(i).printOptionScoreKey()); //scoring rs.updateObject(4, ItemType.toInt(variables.get(i).getType().getItemType())); //item type rs.updateObject(5, DataType.toInt(variables.get(i).getType().getDataType())); //data type rs.updateObject(6, variables.get(i).getLabel().toString()); //label rs.updateRow(); } rs.close(); stmt.close(); rs=null; stmt=null; dataModified = 0; this.firePropertyChange("message", "", "Variables saved"); }catch(SQLException ex){ logger.fatal(ex.getMessage(), ex); this.firePropertyChange("error", "", "Error - Check log for details."); } } } public Object getValueAt(int r, int c){ VariableAttributes varInfo = variables.get(r); if(c==0){ return varInfo.getName().toString(); }else if(c==1){//convert integer to string for display return varInfo.getType().getItemTypeString(); }else if(c==2){ return varInfo.printOptionScoreKey(); }else if(c==3){ return varInfo.getItemGroup(); }else if(c==4){ return varInfo.getLabel().toString(); }else if(c==5){ return varInfo.getSpecialDataCodes().getOmittedCode(); }else if(c==6){ return varInfo.getSpecialDataCodes().getNotReachedCode(); } return null;//c is too big } // columnNames = {"Variable", "Type", "scoring", "Group", "Label"}; @Override public void setValueAt(Object value, int r, int c){ //c==0 not editable VariableAttributes varInfo = variables.get(r); String valueString = value.toString(); if(c==1){ if(!varInfo.getType().getItemTypeString().equals(valueString)){ dataModified++; if(valueString.equals(ItemType.CONTINUOUS_ITEM.toString()) ) { varInfo.clearCategory(); varInfo.getType().setItemType(valueString); }else{ varInfo.getType().setItemType(valueString); } updateTestItemOrder(); } }else if(c==2){ if(!varInfo.printOptionScoreKey().equals(valueString)){ dataModified++; varInfo.clearCategory(); if((valueString==null || value.toString().equals("")) && varInfo.getType().getItemType()!=ItemType.CONTINUOUS_ITEM){ setValueAt(ItemType.CONTINUOUS_ITEM.toString(), r, 1); }else{ varInfo.addAllCategories(value.toString());//Type changed in VarInfo here by call to addAllCategories() } } }else if(c==3){ if(!varInfo.getItemGroup().equals(valueString)){ dataModified++; varInfo.setItemGroup(valueString); } }else if(c==4){ if(!varInfo.getLabel().toString().equals(valueString)){ dataModified++; varInfo.setLabel(valueString); } } fireVariableChangeEvent(new VariableChangeEvent(this, tableName, varInfo, VariableChangeType.VARIABLE_MODIFIED)); } public int getRowCount(){ return numRows; } public int getColumnCount(){ return columnNames.length; } @Override public String getColumnName(int c){ return columnNames[c]; } @Override public Class getColumnClass(int c){ return String.class; } @Override public boolean isCellEditable(int row, int col){ if(col==3 || col==4) return true; return false; } public ArrayList<VariableAttributes> getVariables(){ return variables; } public VariableAttributes getVariableAt(int index){ return variables.get(index); } //=============================================================================================================== //Process messages here //=============================================================================================================== public synchronized void addPropertyChangeListener(PropertyChangeListener l){ propertyChangeListeners.add(l); } public synchronized void removePropertyChangeListener(PropertyChangeListener l){ propertyChangeListeners.remove(l); } protected synchronized void firePropertyChange(String propertyName, Object oldValue, Object newValue){ PropertyChangeEvent e = new PropertyChangeEvent(this, propertyName, oldValue, newValue); for(PropertyChangeListener l : propertyChangeListeners){ l.propertyChange(e); } } //=============================================================================================================== //=============================================================================================================== //Handle variable changes here // -Dialogs should listen for changes to variables // -Dialogs will use these methods to add their variable listeners //=============================================================================================================== public synchronized void addVariableChangeListener(VariableChangeListener l){ variableChangeListeners.add(l); } public synchronized void removeVariableChangeListener(VariableChangeListener l){ variableChangeListeners.remove(l); } public synchronized void removeAllVariableChangeListeners(){ for(VariableChangeListener l : variableChangeListeners){ variableChangeListeners.remove(l); } } public synchronized void fireVariableChangeEvent(VariableChangeEvent e){ for(VariableChangeListener l : variableChangeListeners){ l.variableChanged(e); } } //=============================================================================================================== //=============================================================================================================== //Handle database changes here // -Dialogs should listen for new table opened // -Dialogs will use these methods to add their variable listeners //=============================================================================================================== public synchronized void addDatabaseChangeListener(DatabaseSelectionListener l){ databaseChangeListeners.add(l); } public synchronized void removeVariableChangeListener(DatabaseSelectionListener l){ databaseChangeListeners.remove(l); } public synchronized void removeAllDatabaseChangeListeners(){ for(DatabaseSelectionListener l : databaseChangeListeners){ databaseChangeListeners.remove(l); } } public synchronized void fireDatabaseChangeEvent(DatabaseSelectionEvent e){ for(DatabaseSelectionListener l : databaseChangeListeners){ l.databaseSelectionChanged(e); } } //=============================================================================================================== }