Home | History | Annotate | Download | only in xmlGenerator
      1 #!/usr/bin/python2
      2 # -*-coding:utf-8 -*
      3 
      4 # Copyright (c) 2011-2014, Intel Corporation
      5 # All rights reserved.
      6 #
      7 # Redistribution and use in source and binary forms, with or without modification,
      8 # are permitted provided that the following conditions are met:
      9 #
     10 # 1. Redistributions of source code must retain the above copyright notice, this
     11 # list of conditions and the following disclaimer.
     12 #
     13 # 2. Redistributions in binary form must reproduce the above copyright notice,
     14 # this list of conditions and the following disclaimer in the documentation and/or
     15 # other materials provided with the distribution.
     16 #
     17 # 3. Neither the name of the copyright holder nor the names of its contributors
     18 # may be used to endorse or promote products derived from this software without
     19 # specific prior written permission.
     20 #
     21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
     22 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     23 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     24 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
     25 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     26 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
     28 # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31 
     32 
     33 
     34 import re
     35 import sys
     36 import copy
     37 from itertools import izip
     38 from itertools import imap
     39 
     40 # =====================================================================
     41 """ Context classes, used during propagation and the "to PFW script" step """
     42 # =====================================================================
     43 
     44 class PropagationContextItem(list) :
     45     """Handle an item during the propagation step"""
     46     def __copy__(self):
     47         """C.__copy__() -> a shallow copy of C"""
     48         return self.__class__(self)
     49 
     50 class PropagationContextElement(PropagationContextItem) :
     51     """Handle an Element during the propagation step"""
     52     def getElementsFromName(self, name):
     53         matchingElements = []
     54         for element in self :
     55             if element.getName() == name :
     56                 matchingElements.append(element)
     57         return matchingElements
     58 
     59 
     60 class PropagationContextOption(PropagationContextItem) :
     61     """Handle an Option during the propagation step"""
     62     def getOptionItems (self, itemName):
     63         items = []
     64         for options in self :
     65             items.append(options.getOption(itemName))
     66         return items
     67 
     68 
     69 class PropagationContext() :
     70     """Handle the context during the propagation step"""
     71     def __init__(self, propagationContext=None) :
     72 
     73         if propagationContext == None :
     74             self._context = {
     75                 "DomainOptions" : PropagationContextOption() ,
     76                 "Configurations" : PropagationContextElement() ,
     77                 "ConfigurationOptions" : PropagationContextOption() ,
     78                 "Rules" : PropagationContextElement() ,
     79                 "PathOptions" : PropagationContextOption() ,
     80         }
     81         else :
     82             self._context = propagationContext
     83 
     84     def copy(self):
     85         """return a copy of the context"""
     86         contextCopy = self._context.copy()
     87 
     88         for key in iter(self._context) :
     89             contextCopy[key] = contextCopy[key].__copy__()
     90 
     91         return self.__class__(contextCopy)
     92 
     93     def getDomainOptions (self):
     94         return self._context["DomainOptions"]
     95 
     96     def getConfigurations (self):
     97         return self._context["Configurations"]
     98 
     99     def getConfigurationOptions (self):
    100         return self._context["ConfigurationOptions"]
    101 
    102     def getRules (self):
    103         return self._context["Rules"]
    104 
    105     def getPathOptions (self):
    106         return self._context["PathOptions"]
    107 
    108 
    109 # =====================================================
    110 """Element option container"""
    111 # =====================================================
    112 
    113 class Options () :
    114     """handle element options"""
    115     def __init__(self, options=[], optionNames=[]) :
    116         self.options = dict(izip(optionNames, options))
    117         # print(options,optionNames,self.options)
    118 
    119 
    120     def __str__(self) :
    121         ops2str = []
    122         for name, argument in self.options.items() :
    123             ops2str.append(str(name) + "=\"" + str(argument) + "\"")
    124 
    125         return " ".join(ops2str)
    126 
    127     def getOption(self, name):
    128         """get option by its name, if it does not exist return empty string"""
    129         return self.options.get(name, "")
    130 
    131     def setOption(self, name, newOption):
    132         """set option by its name"""
    133         self.options[name] = newOption
    134 
    135     def copy (self):
    136         """D.copy() -> a shallow copy of D"""
    137         copy = Options()
    138         copy.options = self.options.copy()
    139         return copy
    140 
    141 # ====================================================
    142 """Definition of all element class"""
    143 # ====================================================
    144 
    145 class Element(object):
    146     """ implement a basic element
    147 
    148     It is the class base for all other elements as Domain, Configuration..."""
    149     tag = "unknown"
    150     optionNames = ["Name"]
    151     childWhiteList = []
    152     optionDelimiter = " "
    153 
    154     def __init__(self, line=None) :
    155 
    156         if line == None :
    157             self.option = Options([], self.optionNames)
    158         else :
    159             self.option = self.optionFromLine(line)
    160 
    161         self.children = []
    162 
    163     def optionFromLine(self, line) :
    164         # get ride of spaces
    165         line = line.strip()
    166 
    167         options = self.extractOptions(line)
    168 
    169         return Options(options, self.optionNames)
    170 
    171     def extractOptions(self, line) :
    172         """return the line splited by the optionDelimiter atribute
    173 
    174         Option list length is less or equal to the optionNames list length
    175         """
    176         options = line.split(self.optionDelimiter, len(self.optionNames) - 1)
    177 
    178         # get ride of leftover spaces
    179         optionsStrip = list(imap(str.strip, options))
    180 
    181         return optionsStrip
    182 
    183     def addChild(self, child, append=True) :
    184         """ A.addChid(B) -> add B to A child list if B class name is in A white List"""
    185         try:
    186             # Will raise an exception if this child is not in the white list
    187             self.childWhiteList.index(child.__class__.__name__)
    188             # If no exception was raised, add child to child list
    189 
    190             if append :
    191                 self.children.append(child)
    192             else :
    193                 self.children.insert(0, child)
    194 
    195         except ValueError:
    196             # the child class is not in the white list
    197             raise ChildNotPermitedError("", self, child)
    198 
    199     def addChildren(self, children, append=True) :
    200         """Add a list of child"""
    201         if append:
    202             # Add children at the end of the child list
    203             self.children.extend(children)
    204         else:
    205             # Add children at the begining of the child list
    206             self.children = children + self.children
    207 
    208     def childrenToString(self, prefix=""):
    209         """return raw printed children """
    210         body = ""
    211         for child in self.children :
    212             body = body + child.__str__(prefix)
    213 
    214         return body
    215 
    216     def __str__(self, prefix="") :
    217         """return raw printed element"""
    218         selfToString = prefix + " " + self.tag + " " + str(self.option)
    219         return selfToString + "\n" + self.childrenToString(prefix + "\t")
    220 
    221     def extractChildrenByClass(self, classTypeList) :
    222         """return all children whose class is in the list argument
    223 
    224         return a list of all children whose class in the list "classTypeList" (second arguments)"""
    225         selectedChildren = []
    226 
    227         for child in  self.children :
    228             for classtype in classTypeList :
    229                 if child.__class__ == classtype :
    230                     selectedChildren.append(child)
    231                     break
    232         return selectedChildren
    233 
    234     def propagate (self, context=PropagationContext()):
    235         """call the propagate method of all children"""
    236         for child in  self.children :
    237             child.propagate(context)
    238 
    239     def getName(self):
    240         """return name option value. If none return "" """
    241         return self.option.getOption("Name")
    242 
    243     def setName(self, name):
    244         self.option.setOption("Name", name)
    245 
    246     def translate(self, translator):
    247         for child in self.children:
    248             child.translate(translator)
    249 
    250 # ----------------------------------------------------------
    251 
    252 class ElementWithTag (Element):
    253     """Element of this class are declared with a tag  => line == "tag: .*" """
    254     def extractOptions(self, line) :
    255         lineWithoutTag = line.split(":", 1)[-1].strip()
    256         options = super(ElementWithTag, self).extractOptions(lineWithoutTag)
    257         return options
    258 
    259 # ----------------------------------------------------------
    260 
    261 class ElementWithInheritance(Element):
    262     def propagate (self, context=PropagationContext) :
    263         """propagate some proprieties to children"""
    264 
    265         # copy the context so that everything that hapend next will only affect
    266         # children
    267         contextCopy = context.copy()
    268 
    269         # check for inheritance
    270         self.Inheritance(contextCopy)
    271 
    272         # call the propagate method of all children
    273         super(ElementWithInheritance, self).propagate(contextCopy)
    274 
    275 
    276 class ElementWithRuleInheritance(ElementWithInheritance):
    277     """class that will give to its children its rules"""
    278     def ruleInheritance(self, context):
    279         """Add its rules to the context and get context rules"""
    280 
    281         # extract all children rule and operator
    282         childRules = self.extractChildrenByClass([Operator, Rule])
    283 
    284         # get context rules
    285         contextRules = context.getRules()
    286 
    287         # adopt rules of the beginning of the context
    288         self.addChildren(contextRules, append=False)
    289 
    290         # add previously extract rules to the context
    291         contextRules += childRules
    292 
    293 
    294 # ----------------------------------------------------------
    295 
    296 class EmptyLine (Element) :
    297     """This class represents an empty line.
    298 
    299     Will raise "EmptyLineWarning" exception at instanciation."""
    300 
    301     tag = "emptyLine"
    302     match = re.compile(r"[ \t]*\n?$").match
    303     def __init__ (self, line):
    304        raise EmptyLineWarning(line)
    305 
    306 # ----------------------------------------------------------
    307 
    308 class Commentary(Element):
    309     """This class represents a commentary.
    310 
    311     Will raise "CommentWarning" exception at instanciation."""
    312 
    313     tag = "commentary"
    314     optionNames = ["comment"]
    315     match = re.compile(r"#").match
    316     def __init__ (self, line):
    317        raise CommentWarning(line)
    318 
    319 # ----------------------------------------------------------
    320 
    321 class Path (ElementWithInheritance) :
    322     """class implementing the "path = value" concept"""
    323     tag = "path"
    324     optionNames = ["Name", "value"]
    325     match = re.compile(r".+=").match
    326     optionDelimiter = "="
    327 
    328     def translate(self, translator):
    329         translator.setParameter(self.getName(), self.option.getOption("value"))
    330 
    331     def Inheritance (self, context) :
    332         """check for path name inheritance"""
    333         self.OptionsInheritance(context)
    334 
    335     def OptionsInheritance (self, context) :
    336         """make configuration name inheritance """
    337 
    338         context.getPathOptions().append(self.option.copy())
    339         self.setName("/".join(context.getPathOptions().getOptionItems("Name")))
    340 
    341 
    342 class GroupPath (Path, ElementWithTag) :
    343     tag = "component"
    344     match = re.compile(tag + r" *:").match
    345     optionNames = ["Name"]
    346     childWhiteList = ["Path", "GroupPath"]
    347 
    348     def getPathNames (self) :
    349         """Return the list of all path child name"""
    350 
    351         pathNames = []
    352 
    353         paths = self.extractChildrenByClass([Path])
    354         for path in paths :
    355             pathNames.append(path.getName())
    356 
    357         groupPaths = self.extractChildrenByClass([GroupPath])
    358         for groupPath in groupPaths :
    359             pathNames += groupPath.getPathNames()
    360 
    361         return pathNames
    362 
    363     def translate(self, translator):
    364         for child in self.extractChildrenByClass([Path, GroupPath]):
    365             child.translate(translator)
    366 
    367 # ----------------------------------------------------------
    368 
    369 class Rule (Element) :
    370     """class implementing the rule concept
    371 
    372     A rule is composed of a criterion, a rule type and an criterion state.
    373     It should not have any child and is propagated to all configuration in parent descendants.
    374     """
    375 
    376     tag = "rule"
    377     optionNames = ["criterion", "type", "element"]
    378     match = re.compile(r"[a-zA-Z0-9_.]+ +(Is|IsNot|Includes|Excludes) +[a-zA-Z0-9_.]+").match
    379     childWhiteList = []
    380 
    381     def PFWSyntax (self, prefix=""):
    382 
    383         script = prefix + \
    384                     self.option.getOption("criterion") + " " + \
    385                     self.option.getOption("type") + " " + \
    386                     self.option.getOption("element")
    387 
    388         return script
    389 
    390 
    391 class Operator (Rule) :
    392     """class implementing the operator concept
    393 
    394     An operator contains rules and other operators
    395     It is as rules propagated to all configuration children in parent descendants.
    396     It should only have the name ANY or ALL to be understood by PFW.
    397     """
    398 
    399     tag = "operator"
    400     optionNames = ["Name"]
    401     match = re.compile(r"ANY|ALL").match
    402     childWhiteList = ["Rule", "Operator"]
    403 
    404     syntax = { "ANY" : "Any" , "ALL" : "All"}
    405 
    406     def PFWSyntax (self, prefix=""):
    407         """ return a pfw rule (ex : "Any{criterion1 is state1}") generated from "self" and its children options"""
    408         script = ""
    409 
    410         script += prefix + \
    411                     self.syntax[self.getName()] + "{ "
    412 
    413         rules = self.extractChildrenByClass([Rule, Operator])
    414 
    415         PFWRules = []
    416         for rule in rules :
    417             PFWRules.append(rule.PFWSyntax(prefix + "    "))
    418 
    419         script += (" , ").join(PFWRules)
    420 
    421         script += prefix + " }"
    422 
    423         return script
    424 
    425 # ----------------------------------------------------------
    426 
    427 class Configuration (ElementWithRuleInheritance, ElementWithTag) :
    428     tag = "configuration"
    429     optionNames = ["Name"]
    430     match = re.compile(r"conf *:").match
    431     childWhiteList = ["Rule", "Operator", "Path", "GroupPath"]
    432 
    433     def composition (self, context):
    434         """make all needed composition
    435 
    436         Composition is the fact that group configuration with the same name defined
    437         in a parent will give their rule children to this configuration
    438         """
    439 
    440         name = self.getName()
    441         sameNameConf = context.getConfigurations().getElementsFromName(name)
    442 
    443         sameNameConf.reverse()
    444 
    445         for configuration in sameNameConf :
    446             # add same name configuration rule children to self child list
    447             self.addChildren(configuration.extractChildrenByClass([Operator, Rule]), append=False)
    448 
    449 
    450     def propagate (self, context=PropagationContext) :
    451         """propagate proprieties to children
    452 
    453         make needed compositions, join ancestor name to its name,
    454         and add rules previously defined rules"""
    455 
    456         # make all needed composition
    457         self.composition(context)
    458 
    459         super(Configuration, self).propagate(context)
    460 
    461     def Inheritance (self, context) :
    462         """make configuration name and rule inheritance"""
    463         # check for configuration name inheritance
    464         self.OptionsInheritance(context)
    465 
    466         # check for rule inheritance
    467         self.ruleInheritance(context)
    468 
    469     def OptionsInheritance (self, context) :
    470         """make configuration name inheritance """
    471 
    472         context.getConfigurationOptions().append(self.option.copy())
    473         self.setName(".".join(context.getConfigurationOptions().getOptionItems("Name")))
    474 
    475 
    476     def getRootPath (self) :
    477 
    478         paths = self.extractChildrenByClass([Path, GroupPath])
    479 
    480         rootPath = GroupPath()
    481         rootPath.addChildren(paths)
    482 
    483         return rootPath
    484 
    485     def getConfigurableElements (self) :
    486         """return all path name defined in this configuration"""
    487 
    488         return self.getRootPath().getPathNames()
    489 
    490     def getRuleString(self):
    491         """Output this configuration's rule as a string"""
    492 
    493         # Create a rootRule
    494         ruleChildren = self.extractChildrenByClass([Rule, Operator])
    495 
    496         # Do not create a root rule if there is only one fist level Operator rule
    497         if len(ruleChildren) == 1 and ruleChildren[0].__class__ == Operator :
    498             ruleroot = ruleChildren[0]
    499 
    500         else :
    501             ruleroot = Operator()
    502             ruleroot.setName("ALL")
    503             ruleroot.addChildren(ruleChildren)
    504 
    505         return ruleroot.PFWSyntax()
    506 
    507     def translate(self, translator):
    508         translator.createConfiguration(self.getName())
    509         translator.setRule(self.getRuleString())
    510 
    511         paths = self.extractChildrenByClass([Path, GroupPath])
    512         translator.setElementSequence(self.getConfigurableElements())
    513         for path in paths:
    514             path.translate(translator)
    515 
    516     def copy (self) :
    517         """return a shallow copy of the configuration"""
    518 
    519         # create configuration or subclass copy
    520         confCopy = self.__class__()
    521 
    522         # add children
    523         confCopy.children = list(self.children)
    524 
    525         # add option
    526         confCopy.option = self.option.copy()
    527 
    528         return confCopy
    529 
    530 class GroupConfiguration (Configuration) :
    531     tag = "GroupConfiguration"
    532     optionNames = ["Name"]
    533     match = re.compile(r"(supConf|confGroup|confType) *:").match
    534     childWhiteList = ["Rule", "Operator", "GroupConfiguration", "Configuration", "GroupPath"]
    535 
    536     def composition (self, context) :
    537         """add itself in context for configuration composition
    538 
    539         Composition is the fact that group configuration with the same name defined
    540         in a parent will give their rule children to this configuration
    541         """
    542 
    543         # copyItself
    544         selfCopy = self.copy()
    545 
    546         # make all needed composition
    547         super(GroupConfiguration, self).composition(context)
    548 
    549         # add the copy in context for futur configuration composition
    550         context.getConfigurations().append(selfCopy)
    551 
    552 
    553     def getConfigurableElements (self) :
    554         """return a list. Each elements consist of a list of configurable element of a configuration
    555 
    556         return a list consisting of all configurable elements for each configuration.
    557         These configurable elements are organized in a list"""
    558         configurableElements = []
    559 
    560         configurations = self.extractChildrenByClass([Configuration])
    561         for configuration in configurations :
    562             configurableElements.append(configuration.getConfigurableElements())
    563 
    564         groudeConfigurations = self.extractChildrenByClass([GroupConfiguration])
    565         for groudeConfiguration in groudeConfigurations :
    566             configurableElements += groudeConfiguration.getConfigurableElements()
    567 
    568         return configurableElements
    569 
    570     def translate(self, translator):
    571         for child in self.extractChildrenByClass([Configuration, GroupConfiguration]):
    572             child.translate(translator)
    573 
    574 # ----------------------------------------------------------
    575 
    576 class Domain (ElementWithRuleInheritance, ElementWithTag) :
    577     tag = "domain"
    578     sequenceAwareKeyword = "sequenceAware"
    579 
    580     match = re.compile(r"domain *:").match
    581     optionNames = ["Name", sequenceAwareKeyword]
    582     childWhiteList = ["Configuration", "GroupConfiguration", "Rule", "Operator"]
    583 
    584     def propagate (self, context=PropagationContext) :
    585         """ propagate name, sequenceAwareness and rule to children"""
    586 
    587         # call the propagate method of all children
    588         super(Domain, self).propagate(context)
    589 
    590         self.checkConfigurableElementUnicity()
    591 
    592     def Inheritance (self, context) :
    593         """check for domain name, sequence awarness and rules inheritance"""
    594         # check for domain name and sequence awarness inheritance
    595         self.OptionsInheritance(context)
    596 
    597         # check for rule inheritance
    598         self.ruleInheritance(context)
    599 
    600     def OptionsInheritance(self, context) :
    601         """ make domain name and sequence awareness inheritance
    602 
    603         join to the domain name all domain names defined in context and
    604         if any domain in context is sequence aware, set sequenceAwareness to True"""
    605 
    606         # add domain options to context
    607         context.getDomainOptions().append(self.option.copy())
    608 
    609         # set name to the junction of all domain name in context
    610         self.setName(".".join(context.getDomainOptions().getOptionItems("Name")))
    611 
    612         # get sequenceAwareness of all domains in context
    613         sequenceAwareList = context.getDomainOptions().getOptionItems(self.sequenceAwareKeyword)
    614         # or operation on all booleans in sequenceAwareList
    615         sequenceAwareness = False
    616         for sequenceAware in sequenceAwareList :
    617             sequenceAwareness = sequenceAwareness or sequenceAware
    618         # current domain sequenceAwareness = sequenceAwareness
    619         self.option.setOption(self.sequenceAwareKeyword, sequenceAwareness)
    620 
    621 
    622     def extractOptions(self, line) :
    623         """Extract options from the definition line"""
    624         options = super(Domain, self).extractOptions(line)
    625 
    626         sequenceAwareIndex = self.optionNames.index(self.sequenceAwareKeyword)
    627 
    628         # translate the keyword self.sequenceAwareKeyword if specified to boolean True,
    629         # to False otherwise
    630         try :
    631             if options[sequenceAwareIndex] == self.sequenceAwareKeyword :
    632                options[sequenceAwareIndex] = True
    633             else:
    634                options[sequenceAwareIndex] = False
    635         except IndexError :
    636             options = options + [None] * (sequenceAwareIndex - len(options)) + [False]
    637         return options
    638 
    639     def getRootConfiguration (self) :
    640         """return the root configuration group"""
    641         configurations = self.extractChildrenByClass([Configuration, GroupConfiguration])
    642 
    643         configurationRoot = GroupConfiguration()
    644 
    645         configurationRoot.addChildren(configurations)
    646 
    647         return configurationRoot
    648 
    649     # TODO: don't do that in the parser, let the PFW tell you that
    650     def checkConfigurableElementUnicity (self):
    651         """ check that all configurable elements defined in child configuration are the sames"""
    652 
    653         # get a list. Each elements of is the configurable element list of a configuration
    654         configurableElementsList = self.getRootConfiguration().getConfigurableElements()
    655 
    656         # if at least two configurations in the domain
    657         if len(configurableElementsList) > 1 :
    658 
    659             # get first configuration configurable element list sort
    660             configurableElementsList0 = list(configurableElementsList[0])
    661             configurableElementsList0.sort()
    662 
    663             for configurableElements in configurableElementsList :
    664                 # sort current configurable element list
    665                 auxConfigurableElements = list(configurableElements)
    666                 auxConfigurableElements.sort()
    667 
    668                 if auxConfigurableElements != configurableElementsList0 :
    669                     # if different, 2 configurations those not have the same configurable element list
    670                     # => one or more configurable element is missing in one of the 2 configuration
    671                     raise UndefinedParameter(self.getName())
    672 
    673 
    674     def translate(self, translator):
    675         sequence_aware = self.option.getOption(self.sequenceAwareKeyword)
    676         translator.createDomain(self.getName(), sequence_aware)
    677 
    678         configurations = self.getRootConfiguration()
    679         configurableElementsList = configurations.getConfigurableElements()
    680 
    681         # add configurable elements
    682         if len(configurableElementsList) != 0 :
    683             for configurableElement in configurableElementsList[0] :
    684                 translator.addElement(configurableElement)
    685 
    686         configurations.translate(translator)
    687 
    688 class GroupDomain (Domain) :
    689     tag = "groupDomain"
    690     match = re.compile(r"(supDomain|domainGroup) *:").match
    691     childWhiteList = ["GroupDomain", "Domain", "GroupConfiguration", "Rule", "Operator"]
    692 
    693     def translate(self, translator):
    694         for child in self.extractChildrenByClass([Domain, GroupDomain]):
    695             child.translate(translator)
    696 
    697 # ----------------------------------------------------------
    698 
    699 class Root(Element):
    700     tag = "root"
    701     childWhiteList = ["Domain", "GroupDomain"]
    702 
    703 
    704 # ===========================================
    705 """ Syntax error Exceptions"""
    706 # ===========================================
    707 
    708 class MySyntaxProblems(SyntaxError) :
    709     comment = "syntax error in %(line)s "
    710 
    711     def __init__(self, line=None, num=None):
    712         self.setLine(line, num)
    713 
    714     def __str__(self):
    715 
    716         if self.line :
    717             self.comment = self.comment % {"line" : repr(self.line)}
    718         if self.num :
    719             self.comment = "Line " + str(self.num) + ", " + self.comment
    720         return self.comment
    721 
    722     def setLine (self, line, num):
    723         self.line = str(line)
    724         self.num = num
    725 
    726 
    727 # ---------------------------------------------------------
    728 
    729 class MyPropagationError(MySyntaxProblems) :
    730     """ Syntax error Exceptions used in the propagation step"""
    731     pass
    732 
    733 class UndefinedParameter(MyPropagationError) :
    734     comment = "Configurations in domain '%(domainName)s' do not all set the same parameters "
    735     def __init__ (self, domainName):
    736         self.domainName = domainName
    737     def __str__ (self):
    738         return self.comment % { "domainName" : self.domainName }
    739 
    740 
    741 # -----------------------------------------------------
    742 """ Syntax error Exceptions used by parser"""
    743 
    744 class MySyntaxError(MySyntaxProblems) :
    745     """ Syntax error Exceptions used by parser"""
    746     pass
    747 
    748 class MySyntaxWarning(MySyntaxProblems) :
    749     """ Syntax warning Exceptions used by parser"""
    750     pass
    751 
    752 class IndentationSyntaxError(MySyntaxError) :
    753     comment = """syntax error in %(line)s has no father element.
    754     You can only increment indentation by one tabutation per line")"""
    755 
    756 class EmptyLineWarning(MySyntaxWarning):
    757     comment = "warning : %(line)s is an empty line and has been ommited"
    758 
    759 class CommentWarning(MySyntaxWarning):
    760     comment = "warning : %(line)s is a commentary and has been ommited"
    761 
    762 class ChildNotPermitedError(MySyntaxError):
    763     def __init__(self, line, fatherElement, childElement):
    764         self.comment = "syntax error in %(line)s, " + fatherElement.tag + " should not have a " + childElement.tag + " child."
    765         super(ChildNotPermitedError, self).__init__(line)
    766 
    767 
    768 class UnknownElementTypeError(MySyntaxError):
    769     comment = " error in line %(line)s , not known element type were matched "
    770 
    771 class SpaceInIndentationError(MySyntaxError):
    772     comment = " error in ,%(line)s space is not permited in indentation"
    773 
    774 
    775 # ============================================
    776 """Class creating the DOM elements from a stream"""
    777 # ============================================
    778 
    779 class ElementsFactory(object)  :
    780     """Element factory, return an instance of the first matching element
    781 
    782     Test each element list in elementClass and instanciate it if it's methode match returns True
    783     The method match is called with input line as argument
    784     """
    785     def __init__ (self):
    786         self.elementClass = [
    787         EmptyLine ,
    788         Commentary,
    789         GroupDomain,
    790         Domain,
    791         Path,
    792         GroupConfiguration,
    793         Configuration,
    794         Operator,
    795         Rule,
    796         GroupPath
    797         ]
    798 
    799     def createElementFromLine (self, line) :
    800         """return an instance of the first matching element
    801 
    802         Test each element list in elementClass and instanciate it if it's methode match returns True
    803         The method match is called with the argument line.
    804         Raise UnknownElementTypeError if no element matched.
    805         """
    806         for element in self.elementClass :
    807             if element.match(line) :
    808                 # print (line + element.__class__.__name__)
    809                 return element(line)
    810         # if we have not find any
    811         raise UnknownElementTypeError(line)
    812 
    813 #------------------------------------------------------
    814 
    815 class Parser(object) :
    816     """Class implementing the parser"""
    817     def __init__(self):
    818         self.rankPattern = re.compile(r"^([\t ]*)(.*)")
    819         self.elementFactory = ElementsFactory()
    820         self.previousRank = 0
    821 
    822     def __parseLine__(self, line):
    823 
    824         rank, rest = self.__getRank__(line)
    825 
    826         # instanciate the coresponding element
    827         element = self.elementFactory.createElementFromLine(rest)
    828 
    829         self.__checkIndentation__(rank)
    830 
    831         return rank, element
    832 
    833     def __getRank__(self, line):
    834         """return the rank, the name and the option of the input line
    835 
    836 the rank is the number of tabulation (\t) at the line beginning.
    837 the rest is the rest of the line."""
    838         # split line in rank and rest
    839         rank = self.rankPattern.match(line)
    840         if rank :
    841             rank, rest = rank.group(1, 2)
    842         else :
    843             raise MySyntaxError(line)
    844 
    845         # check for empty line
    846         if rest == "" :
    847             raise EmptyLineWarning(line)
    848 
    849         # check for space in indentation
    850         if rank.find(" ") > -1 :
    851             raise SpaceInIndentationError(line)
    852 
    853         rank = len (rank) + 1  # rank starts at 1
    854 
    855 
    856         return rank, rest
    857 
    858 
    859     def __checkIndentation__(self, rank):
    860         """check if indentation > previous indentation + 1. If so, raise IndentationSyntaxError"""
    861         if (rank > self.previousRank + 1) :
    862             raise IndentationSyntaxError()
    863         self.previousRank = rank
    864 
    865     def parse(self, stream, verbose=False):
    866         """parse a stream, usually a opened file"""
    867         myroot = Root("root")
    868         context = [myroot]  # root is element of rank 0
    869         warnings = ""
    870 
    871         for num, line in enumerate(stream):
    872             try:
    873                 rank, myelement = self.__parseLine__(line)
    874 
    875                 while len(context) > rank :
    876                     context.pop()
    877                 context.append(myelement)
    878                 context[-2].addChild(myelement)
    879 
    880             except MySyntaxWarning, ex:
    881                 ex.setLine(line, num + 1)
    882                 if verbose :
    883                     print >>sys.stderr, ex
    884 
    885             except MySyntaxError, ex :
    886                 ex.setLine(line, num + 1)
    887                 raise
    888 
    889         return myroot
    890 
    891