/* * 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 2 of the License, or (at your option) any later * version. You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.aitools.programd; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.aitools.programd.graph.Nodemapper; import org.aitools.programd.predicates.PredicateInfo; import org.aitools.programd.predicates.PredicateMap; import org.aitools.programd.processor.Processor; import org.aitools.programd.util.InputNormalizer; import org.aitools.programd.util.Substituter; import org.aitools.util.Lists; /** * Handles all of the properties of a bot. * * @author <a href="mailto:noel@aitools.org">Noel Bush</a> * @author Eion Robb */ public class Bot { /** The label for the bot. */ private String _label; /** The files loaded for the bot. */ private Map<URL, Set<Nodemapper>> loadedFiles = new HashMap<URL, Set<Nodemapper>>(); /** The bot's properties. */ private Map<String, String> properties = Collections.checkedMap(new HashMap<String, String>(), String.class, String.class); /** The bot's predicate infos. */ private Map<String, PredicateInfo> predicatesInfo = Collections.checkedMap(new HashMap<String, PredicateInfo>(), String.class, PredicateInfo.class); /** The bot's processor-specific substitution maps. */ private Map<Class<? extends Processor>, LinkedHashMap<Pattern, String>> substitutionMaps = new HashMap<Class<? extends Processor>, LinkedHashMap<Pattern, String>>(); /** The bot's input substitution map. */ private Map<Pattern, String> inputSubstitutions = Collections.checkedMap(new LinkedHashMap<Pattern, String>(), Pattern.class, String.class); /** The bot's sentence splitters. */ private List<String> sentenceSplitters = new ArrayList<String>(); /** The bot's sentence splitters, as a compiled pattern. */ private Pattern sentenceSplitterPattern; /** Holds cached predicates, keyed by userid. */ private Map<String, PredicateMap> predicateCache = Collections.synchronizedMap(new HashMap<String, PredicateMap>()); /** The page to use for this bot when communicating via the servlet interface. */ private String servletPage = ""; /** The files containing test suites. */ private List<URL> testSuites; /** The directory where test reports are to be written. */ private URL testReportDirectory; /** The predicate empty default. */ protected String predicateEmptyDefault; /** * Creates a new Bot with the given label. * * @param label the id to use for the new bot * @param coreSettings the core settings to use */ public Bot(String label, CoreSettings coreSettings) { this._label = label; this.predicateEmptyDefault = coreSettings.getPredicateEmptyDefault(); } /** * Adds an input substitution. The <code>find</code> parameter is stored in uppercase, to do case-insensitive * comparisons. The <code>replace</code> parameter is stored as is. * * @param find the find-string part of the substitution * @param replace the replace-string part of the substitution */ public void addInputSubstitution(Pattern find, String replace) { this.inputSubstitutions.put(find, replace); } /** * Registers some information about a predicate in advance. Not required; just used when it is necessary to specify a * default value for a predicate and/or specify its type as return-name-when-set. * * @param name the name of the predicate * @param defaultValue the default value (if any) for the predicate * @param returnNameWhenSet whether the predicate should return its name when set */ public void addPredicateInfo(String name, String defaultValue, boolean returnNameWhenSet) { PredicateInfo info = new PredicateInfo(name, defaultValue, returnNameWhenSet); this.predicatesInfo.put(name, info); } /** * Adds a sentence splitter to the sentence splitters list. * * @param splitter the string on which to divide sentences */ public void addSentenceSplitter(String splitter) { if (splitter != null) { this.sentenceSplitterPattern = null; this.sentenceSplitters.add(".+?" + splitter); } } /** * Adds a substitution to the indicated map. If the map does not yet exist, it is created. The <code>find</code> * parameter is stored in uppercase, to do case-insensitive comparisons. The <code>replace</code> parameter is stored * as is. * * @param processor the processor with which the map is associated * @param find the find-string part of the substitution * @param replace the replace-string part of the substitution */ public void addSubstitution(Class<? extends Processor> processor, Pattern find, String replace) { if (!this.substitutionMaps.containsKey(processor)) { this.substitutionMaps.put(processor, new LinkedHashMap<Pattern, String>()); } this.substitutionMaps.get(processor).put(find, replace); } /** * Adds a nodemapper to the path map. * * @param path the path * @param nodemapper the mapper for the node to add */ public void addToPathMap(URL path, Nodemapper nodemapper) { Set<Nodemapper> nodemappers = this.loadedFiles.get(path); if (nodemappers == null) { nodemappers = new HashSet<Nodemapper>(); this.loadedFiles.put(path, nodemappers); } nodemappers.add(nodemapper); } /** * Applies input substitutions to the given input * * @param input the input to which to apply substitutions * @return the processed input */ public String applyInputSubstitutions(String input) { return Substituter.applySubstitutions(this.inputSubstitutions, input); } /** * Returns the id of the bot. * * @return the id of the bot */ public String getID() { return this._label; } /** * Returns a map of the files loaded by this bot. * * @return a map of the files loaded by this bot */ public Map<URL, Set<Nodemapper>> getLoadedFilesMap() { return this.loadedFiles; } /** * Returns the predicate cache. * * @return the predicate cache */ public Map<String, PredicateMap> getPredicateCache() { return this.predicateCache; } /** * Returns the predicates info map. * * @return the predicates info map */ public Map<String, PredicateInfo> getPredicatesInfo() { return this.predicatesInfo; } /** * @return the properties */ public Map<String, String> getProperties() { return this.properties; } /** * Retrieves the value of a named bot property. * * @param name the name of the bot property to get * @return the value of the bot property */ public String getPropertyValue(String name) { // Don't bother with empty property names. if (name == null || "".equals(name)) { return this.predicateEmptyDefault; } // Retrieve the contents of the property. String value = this.properties.get(name); if (value != null) { return value; } // (otherwise...) return this.predicateEmptyDefault; } /** * @return the servlet servletPage */ public String getServletPage() { return this.servletPage; } /** * @param processor the processor whose substitution map is desired * @return the substitution map associated with the given processor class. */ public Map<Pattern, String> getSubstitutionMap(Class<? extends Processor> processor) { return this.substitutionMaps.get(processor); } /** * @return Returns the testReportDirectory. */ public URL getTestReportDirectory() { return this.testReportDirectory; } /** * @return Returns the list of test suite files. */ public List<URL> getTestSuites() { return this.testSuites; } /** * Returns whether the bot has loaded the given file(name). * * @param filename the filename to check * @return whether the bot has loaded the given file(name) */ public boolean hasLoaded(String filename) { return this.loadedFiles.containsKey(filename); } /** * Returns the map of predicates for a userid if it is cached, or a new map if it is not cached. * * @param userid * @return the map of predicates for the given userid */ public PredicateMap predicatesFor(String userid) { PredicateMap userPredicates; // Find out if any predicates for this userid are cached. if (!this.predicateCache.containsKey(userid)) { // Create them if not. userPredicates = new PredicateMap(); this.predicateCache.put(userid, userPredicates); } else { userPredicates = this.predicateCache.get(userid); assert userPredicates != null : "userPredicates is null!"; } return userPredicates; } /** * Splits the given input into sentences. * * @param input the input to split * @return the sentences of the input */ public List<String> sentenceSplit(String input) { if (this.sentenceSplitters.size() == 0) { return Lists.singleItem(input); } if (this.sentenceSplitterPattern == null) { this.sentenceSplitterPattern = Pattern.compile(Lists.asRegexAlternatives(this.sentenceSplitters, false), Pattern.DOTALL); } return InputNormalizer.sentenceSplit(this.sentenceSplitterPattern, input); } /** * Sets the bot's properties. * * @param map the properties to set. */ public void setProperties(HashMap<String, String> map) { this.properties = map; } /** * Sets the value of a bot property. * * @param name the name of the bot predicate to set * @param value the value to set */ public void setPropertyValue(String name, String value) { // Property name must not be empty. if (name == null || "".equals(name)) { return; } // Store the property. this.properties.put(name, value); } /** * @param page the servlet servletPage to user */ public void setServletPage(String page) { this.servletPage = page; } /** * @param url The testReportDirectory to set. */ public void setTestReportDirectory(URL url) { this.testReportDirectory = url; } /** * @param files The list of test suite files to set */ public void setTestSuitePathspec(List<URL> files) { this.testSuites = files; } }