source: trunk/game/scripts/dialoguevalidator.py @ 370

Revision 370, 8.1 KB checked in by eliedebrauwer, 10 years ago (diff)

Ticket #147. Patch by eliedebrauwer: Added more verbosity in error reporting, updated unittest to be more verbose as well, added standalone validator utility. fixes[s:trac, t:147]

Line 
1#!/usr/bin/python
2
3#   This file is part of PARPG.
4
5#   PARPG is free software: you can redistribute it and/or modify
6#   it under the terms of the GNU General Public License as published by
7#   the Free Software Foundation, either version 3 of the License, or
8#   (at your option) any later version.
9
10#   PARPG is distributed in the hope that it will be useful,
11#   but WITHOUT ANY WARRANTY; without even the implied warranty of
12#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13#   GNU General Public License for more details.
14
15#   You should have received a copy of the GNU General Public License
16#   along with PARPG.  If not, see <http://www.gnu.org/licenses/>.
17
18import yaml
19import types
20import os
21
22class DialogueFormatException(Exception):
23    """ Exception thrown when the DialogueValidator has encountered an
24    error. """
25    pass
26
27class DialogueValidator(object):
28
29    def validateDialogue(self, tree, topdir):
30        """ Checks whether a given tree is containing a valid dialogue.
31        @type tree
32        @param tree
33        @type topdir: string
34        @param topdir: Top directory where to start searching for files.
35        @raises: dialogueFormatException on error
36        @return: true on success"""
37   
38        self.tree = tree
39        self.topdir = topdir
40       
41        # Test if the required top nodes are present
42        for node in ("NPC", "AVATAR", "START"):
43            if not node in self.tree or not isinstance(self.tree[node],\
44                                                       types.StringType):
45                raise DialogueFormatException("Node: " + node + \
46                                              " not found or invalid type")
47       
48       
49        self.__validateAvatar()
50
51        # Test if the sections node is present
52        if not "SECTIONS" in self.tree or not isinstance(self.tree["SECTIONS"],\
53                                                         types.DictionaryType):
54            raise DialogueFormatException("Node: SECTIONS not found or invalid")
55
56        # Start node should be valid
57        if not self.tree["START"] in self.tree["SECTIONS"]:
58            raise DialogueFormatException("Main section " + self.tree["START"] \
59                                          + " could not be found")
60
61        # Validate all sections
62        for section in self.tree["SECTIONS"]:
63            self.__validateSection(self.tree["SECTIONS"][section], section)
64           
65        return True 
66   
67    def validateDialogueFromFile(self, file_name, topdir="."):
68        """ Checks whether a yaml file is containing a valid dialogue
69        @type file_name: string
70        @param file_name: File name of the yaml file to validate
71        @type topdir: string
72        @param topdir: Top directory where to start searching for files.
73        @raises: DialogueFormatException on error
74        @return: True on success"""
75
76        tree = yaml.load(file(file_name))
77        return self.validateDialogue(tree, topdir)
78
79    def __validateAvatar(self):
80        """ Check that the avatar is an existing file.
81        @raises: DialogueFormatException on error. """
82        fname = os.path.join(self.topdir,self.tree["AVATAR"])
83        if not os.path.isfile(fname):
84            raise DialogueFormatException("Avatar file could not " +\
85                                          "be found: " + fname)
86
87       
88    def __validateSection(self, section, section_name):
89        """ Checks whether a section is a valid section.
90        @type section: dictionary
91        @type section_name: string
92        @param section: Section to validate
93        @param section_name: Name of the section to validate
94        @raises: DialogueFormatException on error """
95
96        for entry in section:
97            for action in entry:
98                # Verify if the commands are known and if their parameters have
99                # the correct type.
100                if action == "say":
101                    if not isinstance(entry[action], types.StringType):
102                        raise DialogueFormatException(
103                            "Section: " + section_name + " has an invalid " +\
104                            action + " node")
105                elif action == "responses":
106                    if not isinstance(entry[action], types.ListType):
107                        raise DialogueFormatException(                       
108                            "Section: " + section_name + " has an invalid " +\
109                            action + " node")
110                    self.__validateResponses(entry["responses"], section_name)
111                elif action == "meet":
112                    if not isinstance(entry[action], types.StringType):
113                        raise DialogueFormatException(\
114                            "Section: " + section_name + " has an invalid " +\
115                            action + " node")
116                    #TODO: verify person
117                elif action in ("complete_quest", "start_quest", \
118                                "delete_quest"):
119                    if not isinstance(entry[action], types.StringType):
120                        raise DialogueFormatException(\
121                            "Section: " + section_name + " has an invalid " +\
122                            action + " node")
123                    #TODO: verify quest
124                elif action in ("get_stuff", "take_stuff"):
125                    if not isinstance(entry[action], types.StringType):
126                        raise DialogueFormatException(\
127                            "Section: " + section_name + " has an invalid " +\
128                            action + " node")
129                    #TODO: verify object
130                elif action in ("increase_value", "decrease_value", \
131                                "set_value"):
132                    if not isinstance(entry[action], types.DictionaryType):
133                        raise DialogueFormatException(\
134                            "Section: " + section_name + " has an invalid " +\
135                            action + " node")                   
136                    #TODO: verify value checks
137                elif action == "dialogue":
138                    if not isinstance(entry[action], types.StringType) \
139                       or not self.__isValidSectionName(entry[action]):
140                        raise DialogueFormatException(\
141                            "Section: " + section_name + " has an invalid " +\
142                            action + " node")
143                else :
144                    raise DialogueFormatException("Section: " + section_name + \
145                                                  " has an unknown action: " +\
146                                                  action)
147           
148
149    def __validateResponses(self, responses, section_name):
150        """ Checks if the list of responses is a valid list.
151        @type responses: List
152        @param respones: A list of response
153        @type section_name: String
154        @param section_name: The section name these responses belong to
155        @raise DialogueFormatException on error
156        @return True When the lists is a valid list"""
157       
158        for option in responses:
159            if not isinstance(option[0], types.StringType):
160                raise DialogueFormatException("Response should be a string")
161           
162            if not self.__isValidSectionName(option[1]):
163                raise DialogueFormatException("Section: " + section_name + \
164                                              " contains an invalid target: " +\
165                                              option[1] + " in response")
166           
167            #TODO: option[2] might be a conditional
168       
169    def __isValidSectionName(self, name):
170        """ Checks if a given name is valid section
171        @type name: string
172        @param name: Name of the section to check
173        @return True when name is a valid section name, False otherwise"""
174       
175        if name=="back" or name=="end":
176            return True
177
178        if name in self.tree["SECTIONS"]:
179            return True;
180
181        # Handle 'back name' used to go back 'n' in the stack.
182        if name.startswith("back "):
183            return self.__isValidSectionName(name.split(" ")[1])
184       
185        return False;
186
Note: See TracBrowser for help on using the repository browser.