Subversion Repositories HomeAutomation

Rev

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
}