/*
 * PCComponent.java
 */
import java.util.*; // for Vector
import java.sql.*;
import java.io.*; // for debug file writer

/** is a class for reading the contents of a database that describes
 * a Personal Computer system and its subcomponents.
* History:
* 2 Oct 2004 : initial version
* 3 Oct 2004 : added indented recursive generation of * toString() data
* 4 Oct 2004 : added ownCost data and special format marker * ^B in toString(). This requires the caller * of toString() to use the ConsoleFormatter * class to set the way toString() data will * appear in a console session. * @author Timothy Paul Fox * @version 04 October 2004 */ public class PCComponent { private String componentName; private String componentType; private Vector partsBin; private String tableName; private String tableKey; private double ownCost; private double aggregateCost; private String partsDescription; { partsBin = null; partsDescription = ""; } /** Creates a new instance of PCComponent * @param dbTable names the database table that contains a row that * describes this component. * @param dbKey is the primary key that identifies the row in dbTable * that contains this component's data. * @param con is the connection to the database that contains dbTable. * @throws Exception is a catch-all for anything that can go wrong * when instantiating the object. */ public PCComponent(String dbTable, String dbKey, Connection con) throws Exception { Statement stmt = null; ResultSet rslt = null; ResultSetMetaData rsmd = null; String subTable, subKey, colName; Vector partFinder; StringBuffer sb; PCComponent subcomponent; int columns, xPos; if (dbTable == null || dbKey == null || con == null) { throw new IllegalArgumentException( "Bad arg to PCComponent()"); } tableKey = dbKey; tableName = dbTable; try { stmt = con.createStatement(); // look up the component type rslt = stmt.executeQuery("SELECT * FROM extrainfo" + " WHERE tname = " + quoteForSQL(dbTable)); rslt.first(); // if no data, throws SQLException. componentType = rslt.getString("classname"); // read the home table rslt = stmt.executeQuery("SELECT * FROM " + dbTable + " WHERE prikey = " + quoteForSQL(dbKey)); rslt.first(); // if no data, throws SQLException. componentName = rslt.getString("name"); ownCost = rslt.getDouble("owncost"); partsBin = new Vector(); sb = new StringBuffer(componentType + ": " + componentName); sb.append("\u0002" + getOwnCost()); // get information about subcomponents, if any partFinder = new Vector(); rsmd = rslt.getMetaData(); columns = rsmd.getColumnCount(); while (columns > 0) { colName = rsmd.getColumnName(columns).toLowerCase(); if (colName.startsWith("fk")) { subKey = rslt.getString(colName); partFinder.insertElementAt( colName.substring(2) + "\t" + subKey, 0); } columns--; } Enumeration en = partFinder.elements(); aggregateCost = getOwnCost(); while (en.hasMoreElements()) { subTable = (String) en.nextElement(); xPos = subTable.indexOf('\t'); subKey = subTable.substring(1 + xPos); subTable = subTable.substring(0, xPos); subcomponent = new PCComponent(subTable, subKey, con); aggregateCost += subcomponent.getCost(); if (sb.charAt(sb.length() - 1) != '\n') sb.append("\n"); sb.append(subcomponent.toString()); partsBin.add(subcomponent); } // build indented description string in sb // to be used by toString() xPos = 0; do { xPos = sb.indexOf("\n", xPos); if (xPos >= 0) { sb.insert(xPos + 1, '\t'); xPos++; } else break; } while (true); partsDescription = sb.toString(); } finally { if (stmt != null) stmt.close(); } } // end constructor PCComponent /** describes this component and its subcomponents. * @return the name of this component and, if it has subcomponents, * the indented names of those subcomponents. */ public String toString() { return partsDescription; } /** Gets the name of this component from the database. * @return the name of this component. */ public String getName() { return componentName; } /** gets the cost of this component, without adding the cost * of subcomponents. * @return the cost of this component. */ public double getOwnCost() { //ong tempL; //tempL = Math.round(ownCost * 1000.0); //return tempL / 1000.0; return ownCost; } /** gets the sum of the costs of this component and its * subcomponents. * @return the aggregate cost. */ public double getCost() { long tempL; tempL = Math.round(aggregateCost * 1000.0); return tempL / 1000.0; } /** tells whether this component has subcomponents. * @return true if this component includes subcomponents. */ public boolean hasSubcomponents() { if (null == partsBin || partsBin.isEmpty()) return false; return true; } /** gets the subcomponents, if any. * @return a Vector of PCComponents, or null. */ public Vector getSubcomponents() { return partsBin; } // NOTE : This method has only been tested with MySQL. private String quoteForSQL(String s) { StringBuffer sb = new StringBuffer(s); int qPos = -1; int lastQPos = 0; do { // escape any internal apostrophes // O'Reilly --> O''Reilly qPos = sb.indexOf("'", lastQPos); if (qPos >= 0) { sb.insert(qPos,'\''); lastQPos = qPos + 2; } } while (qPos >= 0); sb.insert(0, '\''); sb.append('\''); return sb.toString(); } // end quoteForSQL } // end class PCComponent