/*
** 2012 Februar 24
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
*/
package info.ata4.bsplib.app;
import info.ata4.log.LogUtils;
import java.io.InputStream;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
/**
* Source engine application database handler.
*
* @author Nico Bergemann <barracuda415 at yahoo.de>
*/
public class SourceAppDB {
private static final Logger L = LogUtils.getLogger();
private static SourceAppDB instance;
private List<SourceApp> appList = new ArrayList<>();
private Map<Integer, SourceApp> appMap = new HashMap<>();
private float score;
public static SourceAppDB getInstance() {
if (instance == null) {
instance = new SourceAppDB();
}
return instance;
}
private SourceAppDB() {
SAXParserFactory spf = SAXParserFactory.newInstance();
try (InputStream is = getClass().getResourceAsStream("appdb.xml")) {
SAXParser sp = spf.newSAXParser();
SourceAppHandler handler = new SourceAppHandler();
sp.parse(is, handler);
appList = handler.getAppList();
// generate ID map for validation and faster access
for (SourceApp app : appList) {
Integer appID = app.getAppID();
// warn if we have more than one app for an ID
if (appMap.containsKey(appID)) {
L.log(Level.WARNING, "Duplicate App ID {0} for \"{1}\" and \"{2}\"",
new Object[]{appID, appMap.get(appID), app});
}
appMap.put(appID, app);
}
} catch (Exception ex) {
L.log(Level.SEVERE, "Can't load Source application database", ex);
}
}
/**
* Returns the source app for a Steam AppID.
*
* @param appID Steam AppID
* @return the app identifier for the ID or SourceApp.UNKNOWN if it couldn't
* be found.
*/
public SourceApp fromID(int appID) {
if (appMap == null || !appMap.containsKey(appID)) {
return SourceApp.UNKNOWN;
} else {
return appMap.get(appID);
}
}
/**
* Tries to find the AppID though a heuristical search with the given
* parameters.
*
* @param bspName BSP file name
* @param bspVersion BSP file version
* @param classNames Complete set of entity class names
* @return
*/
public SourceApp find(String bspName, int bspVersion, Set<String> classNames) {
SourceApp candidate = SourceApp.UNKNOWN;
score = 0;
if (appList == null) {
return candidate;
}
for (SourceApp app : appList) {
// skip candidates with wrong version
if (app.canCheckVersion() && !app.checkVersion(bspVersion)) {
continue;
}
L.log(Level.FINER, "Testing {0}", app.getName());
float scoreNew = 0;
// check entity class names
if (app.canCheckEntities()) {
scoreNew += app.checkEntities(classNames);
}
// check BSP file name pattern
if (app.canCheckName()) {
scoreNew += app.checkName(bspName);
}
if (scoreNew != 0 && scoreNew > score) {
L.log(Level.FINER, "New candidate {0} with a score of {1}", new Object[]{app.getName(), scoreNew});
candidate = app;
score = scoreNew;
}
}
return candidate;
}
/**
* Returns the total heuristic score for the last call of
* {@link #find(java.lang.String, int, java.util.Set)}.
*
* @return total score
*/
public float getScore() {
return score;
}
/**
* Returns the list of all Source apps from the database.
*
* @return list of Source apps
*/
public List<SourceApp> getAppList() {
return Collections.unmodifiableList(appList);
}
}