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