package hirondelle.predict.main.codes;
import java.util.*;
import java.util.logging.*;
import javax.servlet.ServletContext;
import hirondelle.web4j.model.Id;
import hirondelle.web4j.database.DAOException;
import hirondelle.web4j.database.SqlId;
import hirondelle.web4j.database.Db;
import hirondelle.web4j.util.Util;
import hirondelle.web4j.model.Code;
/**
Fetches code tables from the database upon startup.
<P>Operations performed : <a href='code_table.sql'>SQL statements</a>.
<P>See package summary for important information.
*/
public final class CodeTableUtil {
public static final SqlId FETCH_OUTCOME_CODES = new SqlId("FETCH_OUTCOME_CODES");
/**
Called by the application's implementation of {@link hirondelle.web4j.StartupTasks}, and whenever the
content of a code table changes.
<P>Performs the following :
<ul>
<li>fetch all code tables from the database
<li>store all code tables internally, such that any trivial translations from {@link Id} to {@link Code}
can be performed by this class
<li>place all code tables in application scope, under the keys defined by
{@link CodeTable#getTableName()}, as a {@code List<Code>}. This will allow JSPs to reference code
tables directly by name, without any help from an associated <tt>Action</tt>. (Some might prefer a
<tt>Set</tt> instead of a <tt>List</tt>, but a <tt>Set</tt> would have the disadvantage of hiding any
duplicate items.)
</ul>
*/
public static void init(ServletContext aContext) throws DAOException {
fLogger.fine("Fetching code tables.");
fContext = aContext;
fAllCodeTables = new LinkedHashMap<CodeTable, Map<Id, Code>>();
//this cycling ensures all items in the CodeTable enum are processed
for(CodeTable codeTable: CodeTable.values()){
fetchAndRememberCodeTable(codeTable);
placeInAppScope(codeTable);
}
}
/**
Return a fully populated {@link Code}, given its {@link Id} and {@link CodeTable}.
<P>Called from Model Objects constructors that need to translate a simple {@link Id} for a known
code table into a fully populated {@link Code}.
If <tt>aCodeId</tt> is itself <tt>null</tt>, then <tt>null</tt> is returned.
<P><em>Design Note:</em><br>
This item is package-private. The {@link CodeTable#codeFor(Id, CodeTable)} method is a <tt>public</tt>
version of this method, and simply does a call-forward to this one. The reason is simply to reduce the number
of imports needed in Model Objects that use Codes from 3 to 2 : {@link Code} and {@link CodeTable}.
*/
static Code populate(Id aCodeId, CodeTable aCodeTable){
Code result = null;
Map<Id, Code> codeTable = fAllCodeTables.get(aCodeTable);
if ( codeTable == null ){
throw new RuntimeException("Cannot populate item. Unknown code table " + Util.quote(aCodeTable));
}
if ( aCodeId != null ){
result = codeTable.get(aCodeId);
if ( result == null ){
throw new RuntimeException("Cannot find item in code table " + Util.quote(aCodeTable) + " using Id: " + Util.quote(aCodeId));
}
}
return result;
}
// PRIVATE //
/**
Table for translating a given table/id into a fully populated Code object.
Populated upon startup.
*/
private static Map<CodeTable, Map<Id, Code>> fAllCodeTables;
private static ServletContext fContext;
private static final Logger fLogger = Util.getLogger(CodeTableUtil.class);
private static void fetchAndRememberCodeTable(CodeTable aCodeTable) throws DAOException {
Map<Id, Code> codeTable = null;
if ( CodeTable.OUTCOMES == aCodeTable ){
codeTable = getCodes(FETCH_OUTCOME_CODES);
}
else {
throw new AssertionError("CodeTable not known to CodeTableUtil : " + aCodeTable);
}
fLogger.fine("Code Table " + Util.quote(aCodeTable) + ": " + Util.logOnePerLine(codeTable));
fAllCodeTables.put(aCodeTable, codeTable);
}
/**
Example of the most typical form of code table - from a small database table created
solely to define this code table.
*/
private static Map<Id, Code> getCodes(SqlId aSqlId) throws DAOException {
Map<Id, Code> result = new LinkedHashMap<Id, Code>();
List<Code> codes = Db.list(Code.class, aSqlId);
result = Util.asMap(codes, Id.class, "getId");
return result;
}
private static void placeInAppScope(CodeTable aCodeTable){
Map<Id, Code> codeTable = fAllCodeTables.get(aCodeTable);
Collection<Code> codeValues = codeTable.values();
fContext.setAttribute(aCodeTable.getTableName().toString(), codeValues);
}
}