Go to most recent revision | Details | Last modification | View Log | SVN | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 15 | arune | 1 | /* |
| 2 | * MbModule.java |
||
| 3 | * |
||
| 4 | * Created on den 25 augusti 2003, 01:23 |
||
| 5 | */ |
||
| 6 | package Macbeth.System; |
||
| 7 | |||
| 8 | import org.xml.sax.SAXException; |
||
| 9 | |||
| 10 | import java.io.PrintStream; |
||
| 11 | import java.io.IOException; |
||
| 12 | import java.io.FileNotFoundException; |
||
| 13 | import java.util.LinkedList; |
||
| 14 | import java.util.List; |
||
| 15 | import java.util.HashMap; |
||
| 16 | |||
| 17 | import Macbeth.XML.XMLDataHandler; |
||
| 18 | import Macbeth.XML.XMLParser; |
||
| 19 | import Macbeth.Utilities.TextFormatterStream; |
||
| 20 | import Macbeth.Utilities.DataRepository; |
||
| 21 | |||
| 22 | /** |
||
| 23 | * The Macbeth Module class. Extend this when creating |
||
| 24 | * your own modules. |
||
| 25 | * @author Jimmy |
||
| 26 | */ |
||
| 27 | abstract public class MbModule extends MbComponent implements MbPacketHandler { |
||
| 28 | //our packet XML parser |
||
| 29 | private XMLParser packetXMLParser; |
||
| 30 | //our configuration data handler (if any) |
||
| 31 | private XMLDataHandler configDataHandler; |
||
| 32 | //our packet data handler (if any) |
||
| 33 | private XMLDataHandler packetDataHandler; |
||
| 34 | //our html interface (if any) |
||
| 35 | private MbHTMLInterface htmlInterface; |
||
| 36 | //a list of other modules that we depend on |
||
| 37 | private List dependencies; |
||
| 38 | //our configuration file |
||
| 39 | protected String configFile; |
||
| 40 | //our data repository. stores options and other data from the XML-file |
||
| 41 | protected DataRepository dataRepository; |
||
| 42 | //this points to the options data entry inside the data repository |
||
| 43 | protected DataRepository.DataListItem options; |
||
| 44 | |||
| 45 | /** |
||
| 46 | * Our debug output stream. Write debug-info to this |
||
| 47 | * stream. You can assume that information written |
||
| 48 | * to this stream is not necessarily visible to the |
||
| 49 | * user (only if he/she has turned it on). |
||
| 50 | */ |
||
| 51 | protected PrintStream _debug; |
||
| 52 | |||
| 53 | /** |
||
| 54 | * Our information output stream. Write non-critical |
||
| 55 | * information to this stream. This information will |
||
| 56 | * be visible to the user most of the time (unless |
||
| 57 | * he/she has turned it off). |
||
| 58 | */ |
||
| 59 | protected PrintStream _info; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * Our error output stream. Write all error messages |
||
| 63 | * to this stream. This information will always be |
||
| 64 | * visible to the user. |
||
| 65 | */ |
||
| 66 | protected PrintStream _errors; |
||
| 67 | |||
| 68 | /** |
||
| 69 | * The parent kernel that loaded this module. |
||
| 70 | */ |
||
| 71 | protected MbKernel parentKernel; |
||
| 72 | |||
| 73 | /** |
||
| 74 | * This is always a reference to the packet that |
||
| 75 | * is currently being parsed, so if you need access |
||
| 76 | * to the packet during XML parsing, you can use this. |
||
| 77 | */ |
||
| 78 | protected MbPacket currentPacket; |
||
| 79 | |||
| 80 | /** |
||
| 81 | * Creates a new instance of MbModule. |
||
| 82 | */ |
||
| 83 | public MbModule() { |
||
| 84 | //construct MbComponent |
||
| 85 | super(); |
||
| 86 | parentKernel = null; |
||
| 87 | configDataHandler = null; |
||
| 88 | packetDataHandler = null; |
||
| 89 | currentPacket = null; |
||
| 90 | packetXMLParser = null; |
||
| 91 | htmlInterface = null; |
||
| 92 | configFile = ""; |
||
| 93 | //create dependencies-list |
||
| 94 | dependencies = new LinkedList(); |
||
| 95 | //default streams |
||
| 96 | _debug = System.out; |
||
| 97 | _info = System.out; |
||
| 98 | _errors = System.err; |
||
| 99 | //create data repository |
||
| 100 | dataRepository = new DataRepository(); |
||
| 101 | dataRepository.advancedBuild_BeginList("options"); |
||
| 102 | dataRepository.advancedBuild_BeginListItem(); |
||
| 103 | dataRepository.advancedBuild_EndListItem(); |
||
| 104 | dataRepository.advancedBuild_EndList(); |
||
| 105 | options = (DataRepository.DataListItem)dataRepository.getList("options").firstItem(); |
||
| 106 | //create configuration data handler |
||
| 107 | configDataHandler = new ConfigDataHandler(); |
||
| 108 | } |
||
| 109 | |||
| 110 | /** |
||
| 111 | * Gets the name of the module. |
||
| 112 | * @return The name of the module. |
||
| 113 | */ |
||
| 114 | public abstract String name(); |
||
| 115 | |||
| 116 | /** |
||
| 117 | * Gets a short description of the module. This should |
||
| 118 | * be kept short (1-2 lines) and should be formatted as |
||
| 119 | * plain text. |
||
| 120 | * @return A short description of the component. |
||
| 121 | */ |
||
| 122 | public abstract String description(); |
||
| 123 | |||
| 124 | /** |
||
| 125 | * Selects which XMLDataHandler should take care of the XML-data |
||
| 126 | * found when parsing the configuration file for this module. |
||
| 127 | * @param configDataHandler The XMLDataHandler. |
||
| 128 | * @deprecated Due to new configuration system. Get config data |
||
| 129 | * from data repository instead! |
||
| 130 | */ |
||
| 131 | protected void setConfigDataHandler(XMLDataHandler configDataHandler) { |
||
| 132 | //this.configDataHandler = configDataHandler; |
||
| 133 | } |
||
| 134 | |||
| 135 | /** |
||
| 136 | * Selects which XMLDataHandler should take care of the XML-data |
||
| 137 | * found in incoming packet. |
||
| 138 | * @param packetDataHandler The XMLDataHandler. |
||
| 139 | */ |
||
| 140 | protected void setPacketDataHandler(XMLDataHandler packetDataHandler) { |
||
| 141 | this.packetDataHandler = packetDataHandler; |
||
| 142 | } |
||
| 143 | |||
| 144 | /** |
||
| 145 | * Sets our HTML-interface. |
||
| 146 | * @param htmlInterface The html-interface. |
||
| 147 | */ |
||
| 148 | protected void setHTMLInterface(MbHTMLInterface htmlInterface) { |
||
| 149 | this.htmlInterface = htmlInterface; |
||
| 150 | } |
||
| 151 | |||
| 152 | /** |
||
| 153 | * Retreives this modules HTML-interface. |
||
| 154 | */ |
||
| 155 | public MbHTMLInterface getHTMLInterface() { |
||
| 156 | return htmlInterface; |
||
| 157 | } |
||
| 158 | |||
| 159 | /** |
||
| 160 | * Adds a module dependency. If your module depends on another |
||
| 161 | * modules you should call this with the module names in your |
||
| 162 | * constructor. The kernel will then make sure the dependency |
||
| 163 | * modules are started up before your module. |
||
| 164 | * @param moduleName |
||
| 165 | */ |
||
| 166 | protected final void addDependency(String moduleName) { |
||
| 167 | if (!dependencies.contains(moduleName)) { |
||
| 168 | dependencies.add(moduleName); |
||
| 169 | } |
||
| 170 | } |
||
| 171 | |||
| 172 | /** |
||
| 173 | * Returns this modules dependencies. This is a list containing |
||
| 174 | * the names of all modules that this module depend on. |
||
| 175 | * @return The dependencies-list. |
||
| 176 | */ |
||
| 177 | public List getDependencies() { |
||
| 178 | return dependencies; |
||
| 179 | } |
||
| 180 | |||
| 181 | /** |
||
| 182 | * Returns this modules data repository, which contains all the |
||
| 183 | * module options and its data lists. |
||
| 184 | * @return this modules data repository. |
||
| 185 | */ |
||
| 186 | public DataRepository getDataRepository() { |
||
| 187 | return dataRepository; |
||
| 188 | } |
||
| 189 | |||
| 190 | /** |
||
| 191 | * This method sets default values on all _required_ data |
||
| 192 | * fields in the data repository. |
||
| 193 | */ |
||
| 194 | abstract protected void initDataFields(); |
||
| 195 | |||
| 196 | /** |
||
| 197 | * Will be called when this module should start up. Most |
||
| 198 | * initialization should be done here (rather than in the |
||
| 199 | * constructor). |
||
| 200 | * @throws MbStartupException if this module cannot |
||
| 201 | * start up for some reason. |
||
| 202 | */ |
||
| 203 | public void startup() throws MbStartupException { |
||
| 204 | //this method will set default-values on all required data repository fields |
||
| 205 | initDataFields(); |
||
| 206 | //if there is a packet data handler registered |
||
| 207 | if (packetDataHandler!=null) { |
||
| 208 | //create packet xml parser |
||
| 209 | packetXMLParser = new XMLParser(packetDataHandler); |
||
| 210 | } |
||
| 211 | //if there is a configuration data handler registered |
||
| 212 | if (configDataHandler!=null) { |
||
| 213 | //create XML parser and try to parse configuration file |
||
| 214 | XMLParser xmlParser = new XMLParser(configDataHandler); |
||
| 215 | configFile = "Config\\mod" + name() + ".xml"; |
||
| 216 | try { |
||
| 217 | xmlParser.parseFile(configFile); |
||
| 218 | } catch (FileNotFoundException e) { |
||
| 219 | //no config file existed. that's OK, but warn user |
||
| 220 | // (a config data handler was registered after all) |
||
| 221 | _info.println("WARNING: a configuration data handler exists, but no configuration file was found!"); |
||
| 222 | } catch (IOException e) { |
||
| 223 | //I/O-exception. that's worse, so lets warn |
||
| 224 | _errors.println("I/O-error while trying to read '" + configFile + "':"); |
||
| 225 | _errors.println(e); |
||
| 226 | e.printStackTrace(); |
||
| 227 | } catch (SAXException e) { |
||
| 228 | _errors.println("The configuration file '" + configFile + "' contains syntax errors:"); |
||
| 229 | _errors.println(e); |
||
| 230 | e.printStackTrace(); |
||
| 231 | } |
||
| 232 | } |
||
| 233 | //now that config file is parsed, we can let the packet system start up |
||
| 234 | super.startup(); |
||
| 235 | _debug.println("I was started up!"); |
||
| 236 | } |
||
| 237 | |||
| 238 | /** |
||
| 239 | * Will be called when this module should shut down itself. |
||
| 240 | * You cannot send any packets to other modules here, they |
||
| 241 | * might have been shut down already! |
||
| 242 | */ |
||
| 243 | public void shutdown() { |
||
| 244 | super.shutdown(); |
||
| 245 | _debug.println("I was shut down!"); |
||
| 246 | } |
||
| 247 | |||
| 248 | /** |
||
| 249 | * Sets this module's parent kernel. |
||
| 250 | * @param parentKernel The parent kernel that loaded this module. |
||
| 251 | */ |
||
| 252 | public void setParentKernel(MbKernel parentKernel) { |
||
| 253 | this.parentKernel = parentKernel; |
||
| 254 | _debug = new TextFormatterStream(parentKernel._debug); |
||
| 255 | ((TextFormatterStream)_debug).setPreString(name() + ": "); |
||
| 256 | _info = new TextFormatterStream(parentKernel._info); |
||
| 257 | ((TextFormatterStream)_info).setPreString(name() + ": "); |
||
| 258 | _errors = new TextFormatterStream(parentKernel._errors); |
||
| 259 | ((TextFormatterStream)_errors).setPreString(name() + ": "); |
||
| 260 | } |
||
| 261 | |||
| 262 | /** |
||
| 263 | * Sends a packet to the parent kernel. |
||
| 264 | * @param p The packet that should be sent. |
||
| 265 | */ |
||
| 266 | final public void sendPacket(MbPacket p) { |
||
| 267 | //put our kernel- and module name into the packet source field |
||
| 268 | p.setSource(new MbLocation(parentKernel.name(),this.name())); |
||
| 269 | //tell kernel to receive the packet now |
||
| 270 | parentKernel.packetReceived(p); |
||
| 271 | } |
||
| 272 | |||
| 273 | /** |
||
| 274 | * Will be called when a packet needs to be handled. |
||
| 275 | * Per default, this method invokes the XML-parser |
||
| 276 | * to parse the contents of the packet. |
||
| 277 | * @param p The packet that needs to be handled. |
||
| 278 | */ |
||
| 279 | public void handlePacket(MbPacket p) { |
||
| 280 | //if we have an XML parser for packets |
||
| 281 | if (packetXMLParser!=null) { |
||
| 282 | //set current packet reference |
||
| 283 | currentPacket = p; |
||
| 284 | //try to parse this packets contents |
||
| 285 | try { |
||
| 286 | packetXMLParser.parseString(p.serializeToString()); |
||
| 287 | } catch (SAXException e) { |
||
| 288 | _errors.println("Error while parsing packet. Probably bad syntax!"); |
||
| 289 | _errors.println("Error description was: " + e.getMessage()); |
||
| 290 | _errors.println("The packet was:"); |
||
| 291 | _errors.println(p.serializeToString()); |
||
| 292 | //probably not valid xml data |
||
| 293 | e.printStackTrace(); |
||
| 294 | } catch (IOException e) { |
||
| 295 | e.printStackTrace(); |
||
| 296 | } |
||
| 297 | } |
||
| 298 | } |
||
| 299 | |||
| 300 | |||
| 301 | /** |
||
| 302 | * Performs an action. This is where yor module can expose functionality |
||
| 303 | * for scripts and for the user. |
||
| 304 | * |
||
| 305 | * @param action The Macbeth action that should be performed. |
||
| 306 | * @throws MbActionNotPerformedException If the action ID is invalid. |
||
| 307 | */ |
||
| 308 | public void performAction(MbAction action) throws MbActionNotPerformedException { |
||
| 309 | throw new MbActionNotPerformedException("Unknown action command!"); |
||
| 310 | } |
||
| 311 | |||
| 312 | |||
| 313 | /** |
||
| 314 | * Call this when you want to trigger events from your module. Note that |
||
| 315 | * the event name you provide will automatically be prefixed by the module |
||
| 316 | * name and the string ".On" so that the final event string takes the form |
||
| 317 | * 'modulename.OnXXX" where XXX is the event name you provide. |
||
| 318 | * |
||
| 319 | * @param eventName The name of the event. Please read above. |
||
| 320 | */ |
||
| 321 | private final void triggerEvent(String eventName) { |
||
| 322 | parentKernel.triggerMacbethEvent(name() + ".On" + eventName); |
||
| 323 | } |
||
| 324 | |||
| 325 | |||
| 326 | /** |
||
| 327 | * Takes care of XML-data found in configuration files. |
||
| 328 | */ |
||
| 329 | private class ConfigDataHandler implements XMLDataHandler { |
||
| 330 | /** |
||
| 331 | * Called when start of a new element is found in the XML-data. |
||
| 332 | * @param element The name of the element. |
||
| 333 | * @param attributes The element attributes. |
||
| 334 | */ |
||
| 335 | public void XMLstartElement(String element, HashMap attributes) { |
||
| 336 | if (element.equals("list")) { |
||
| 337 | if (attributes.containsKey("name")) { |
||
| 338 | dataRepository.advancedBuild_BeginList((String)attributes.get("name")); |
||
| 339 | } |
||
| 340 | else { |
||
| 341 | _errors.println("Syntax error in configuration file: 'name'-attribute missing in 'list'-tag!"); |
||
| 342 | } |
||
| 343 | } |
||
| 344 | else if (element.equals("item")) { |
||
| 345 | dataRepository.advancedBuild_BeginListItem(); |
||
| 346 | } |
||
| 347 | else if (element.equals("field")) { |
||
| 348 | if (attributes.containsKey("name") && attributes.containsKey("value")) { |
||
| 349 | dataRepository.advancedBuild_PutField((String)attributes.get("name"), (String)attributes.get("value")); |
||
| 350 | } |
||
| 351 | else { |
||
| 352 | _errors.println("Syntax error in configuration file: 'name' and/or 'value'-attributes missing in 'datafield'-tag!"); |
||
| 353 | } |
||
| 354 | } |
||
| 355 | else if (element.equalsIgnoreCase("option")) { |
||
| 356 | if (attributes.containsKey("name") && attributes.containsKey("value")) { |
||
| 357 | options.putField((String)attributes.get("name"), (String)attributes.get("value")); |
||
| 358 | } |
||
| 359 | else { |
||
| 360 | _errors.println("Syntax error in '" + configFile + "' - 'name'- and/or 'value'-attributes are missing in an 'option'-tag!"); |
||
| 361 | } |
||
| 362 | } |
||
| 363 | } |
||
| 364 | |||
| 365 | public void XMLendElement(String element) { |
||
| 366 | if (element.equals("list")) { |
||
| 367 | dataRepository.advancedBuild_EndList(); |
||
| 368 | } |
||
| 369 | else if (element.equals("item")) { |
||
| 370 | dataRepository.advancedBuild_EndListItem(); |
||
| 371 | } |
||
| 372 | } |
||
| 373 | |||
| 374 | public void XMLelementData(String data) { |
||
| 375 | } |
||
| 376 | |||
| 377 | public void XMLdocumentStart() { |
||
| 378 | } |
||
| 379 | |||
| 380 | public void XMLdocumentEnd() { |
||
| 381 | } |
||
| 382 | } |
||
| 383 | |||
| 384 | } |