1326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard#!/usr/bin/env python3
2326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
3b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# Copyright (c) 2011-2014, Intel Corporation
4b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# All rights reserved.
5fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard#
6b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# Redistribution and use in source and binary forms, with or without modification,
7b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# are permitted provided that the following conditions are met:
8fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard#
9b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# 1. Redistributions of source code must retain the above copyright notice, this
10b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# list of conditions and the following disclaimer.
11b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner#
12b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# 2. Redistributions in binary form must reproduce the above copyright notice,
13b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# this list of conditions and the following disclaimer in the documentation and/or
14b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# other materials provided with the distribution.
15b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner#
16b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# 3. Neither the name of the copyright holder nor the names of its contributors
17b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# may be used to endorse or promote products derived from this software without
18b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# specific prior written permission.
19b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner#
20b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
24b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30b76c9d6de717a9a1cfd94e7a8eca7ee4a2035cd7David Wagner
31fe753d4ba5d5bf3df7b7f4876280b74fc8647285Kevin Rocard
32326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard"""
33326e39e42a801bff4b61655ebc0e3ff759c208ffKevin RocardGenerate a coverage report by parsing parameter framework log.
34326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
35326e39e42a801bff4b61655ebc0e3ff759c208ffKevin RocardThe coverage report contains the:
36326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard - domain
37326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard - configuration
38326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard - rule
39326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard - criterion
40326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardbasic coverage statistics.
41326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard"""
42326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
43326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardimport xml.dom.minidom
44326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardimport sys
45326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardimport re
46326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardimport logging
47326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
48326e39e42a801bff4b61655ebc0e3ff759c208ffKevin RocardFORMAT = '%(levelname)s: %(message)s'
49326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardlogging.basicConfig(stream=sys.stderr, level=logging.WARNING, format=FORMAT)
50326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardlogger = logging.getLogger("Coverage")
51326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
523aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocardclass CustomError(Exception):
53804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    pass
543aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
553aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocardclass ChildError(CustomError):
56804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, parent, child):
57804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.parent = parent
58804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.child = child
593aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
603aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocardclass ChildNotFoundError(ChildError):
61804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __str__(self):
620bb508d358ebe25d61122bde9905db6661591ba0Kevin Rocard        return 'Unable to find the child "%s" in "%s"' % (self.child, self.parent)
633aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
643aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocardclass DuplicatedChildError(ChildError):
65804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __str__(self):
660bb508d358ebe25d61122bde9905db6661591ba0Kevin Rocard        return 'Add existing child "%s" in "%s".' % (self.child, self.parent)
67326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
68326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Element():
69804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """Root class for all coverage elements"""
70804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "element"
71326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
72804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, name):
73326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
74804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.parent = None
75804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.children = []
76326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
77804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.nbUse = 0
78326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
79804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.name = name
80326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
81804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("New element")
82326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
83326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
84804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __str__(self):
85804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return  "%s (%s)" % (self.name, self.tag)
86326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
87804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __eq__(self, compared):
88804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return (self.name == compared.name) and (self.children == compared.children)
89326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
90804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def getName(self, default=""):
91804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.name or default
92326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
93804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def hasChildren(self):
94804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return bool(self.children)
95326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
96804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def getChildren(self):
97804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.children
98326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
99804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getDescendants(self):
100804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children:
101804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            yield child
102804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            for descendant in child._getDescendants() :
103804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                yield descendant
104326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
105804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def getChildFromName(self, childName):
106326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
107804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children :
108326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
109804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            if child.getName() == childName :
110804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                return child
111326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1120bb508d358ebe25d61122bde9905db6661591ba0Kevin Rocard        self.debug('Child "%s" not found' % childName, logging.ERROR)
113326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
114804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Child list :")
115326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
116804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children :
117804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("  - %s" % child)
118326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
119804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        raise ChildNotFoundError(self, childName)
120326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
121326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
122804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def addChild(self, child):
123804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("new child: " + child.name)
124804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.children.append(child)
125804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        child._adoptedBy(self)
126326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
127804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _adoptedBy(self, parent):
128804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        assert(not self.parent)
129804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.parent = parent
130326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
131804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getElementNames(self, elementList):
132804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return (substate.name for substate in elementList)
133326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
134804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _description(self, withCoverage, withNbUse):
135804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        description = self.name
136326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
137804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if withNbUse or withCoverage :
138804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            description += " has been used " + str(self.nbUse) + " time"
139326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
140804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if withCoverage :
141804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            description += self._coverageFormating(self._getCoverage())
142326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
143804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return description
144326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
145326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
146804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getCoverage(self):
147804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """Return the coverage of the element between 0 and 1
148326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
149804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        If the element has no coverage dependency (usually child) return 0 or 1.
150804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        otherwise the element coverage is the dependency coverage average"""
151804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        coverageDependanceElements = list(self._getCoverageDependanceElements())
152326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
153804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        nbcoverageDependence = len(coverageDependanceElements)
154326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
155804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if nbcoverageDependence == 0:
156804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            if self.nbUse == 0:
157804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                return 0
158804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            else:
159804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                return 1
160326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
161804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        coverageDependenceValues = (depElement._getCoverage()
162804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                for depElement in coverageDependanceElements)
163326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
164804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return sum(coverageDependenceValues) / nbcoverageDependence
165326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
166804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getCoverageDependanceElements(self):
167804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.children
168326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
169804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _coverageFormating(self, coverage):
170804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # If no coverage provided
171804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if not coverage :
172804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return ""
173326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
174804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Calculate coverage
175804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return " (%s coverage)" % self._number2percent(coverage)
176326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
177804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    @staticmethod
178804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _number2percent(number):
179804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """Format a number to a integer % string
180326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
181804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        example: _number2percent(0.6666) -> "67%"
182804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """
183804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return "{0:.0f}%".format(100 * number)
184326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
185326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
186804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _dumpDescription(self, withCoverage, withNbUse):
187326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
188804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("yelding description")
189804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        yield RankedLine(self._description(withCoverage, withNbUse), lineSuffix="")
190326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
191804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for dumped in self._dumpPropagate(withCoverage, withNbUse) :
192804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            yield dumped
193326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
194804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _dumpPropagate(self, withCoverage, withNbUse):
195326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
196804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children :
197804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            for dumpedDescription in child._dumpDescription(withCoverage, withNbUse) :
198804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                yield dumpedDescription.increasedRank()
199326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
200326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
201804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def dump(self, withCoverage=False, withNbUse=True):
202326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
203804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return "\n".join(
204804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                str(dumpedDescription) for dumpedDescription in
205804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self._dumpDescription(withCoverage, withNbUse))
206326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
207794fea64ed11b6c2b2604c3a0daa374c885f09d6Kevin Rocard    def exportToXML(self, document, domElement=None):
208804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if domElement == None:
209794fea64ed11b6c2b2604c3a0daa374c885f09d6Kevin Rocard            domElement = document.createElement(self.tag)
21002726ec37a9686572f825520a04700061ffe5d06Kevin Rocard
211804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._XMLaddAttributes(domElement)
212326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
213804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children :
214794fea64ed11b6c2b2604c3a0daa374c885f09d6Kevin Rocard            domElement.appendChild(child.exportToXML(document))
215326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
216804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return domElement
217326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
218804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _XMLaddAttributes(self, domElement):
219804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        attributes = self._getXMLAttributes()
220326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
221804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        coverage = self._getCoverage()
222804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if coverage != None :
223804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            attributes["Coverage"] = self._number2percent(coverage)
224326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
225804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for key, value in attributes.items():
226804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            domElement.setAttribute(key, value)
227326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
228804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getXMLAttributes(self):
229804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return {
230804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "Name": self.name,
231804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "NbUse": str(self.nbUse)
232804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                }
233326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
234804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _incNbUse(self):
235804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.nbUse += 1
236326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
237804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def childUsed(self, child):
238804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._incNbUse()
239804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Propagate to parent
240804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._tellParentThatChildUsed()
241326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
242804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _tellParentThatChildUsed(self):
243804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if self.parent :
244804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.parent.childUsed(self)
245326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
246326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
247804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def parentUsed(self):
248804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._incNbUse()
249804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Propagate to children
250804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children :
251804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            child.parentUsed()
252326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
253804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def hasBeenUsed(self):
254804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.nbUse > 0
255326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
256804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def operationOnChild(self, path, operation):
257326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
258804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if path:
259804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return self._operationPropagate(path, operation)
260804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
261804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("operating on self")
262804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return operation(self)
263326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
264804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _operationPropagate(self, path, operation):
265326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
266804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        childName = path.pop(0)
267804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        child = self.getChildFromName(childName)
268326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
269804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return child.operationOnChild(path, operation)
270326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
271326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
272326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
273804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def debug(self, stringOrFunction, level=logging.DEBUG):
274804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """Print a debug line on stderr in tree form
275326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
276804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        If the debug line is expensive to generate, provide callable
277804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        object, it will be called if log is enable for this level.
278804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        This callable object should return the logline string.
279804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """
280804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if logger.isEnabledFor(level):
281326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
282804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # TODO: use buildin callable if python >= 3.2
283804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            if hasattr(stringOrFunction, "__call__"):
284804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                string = stringOrFunction()
285804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            else:
286804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                string = stringOrFunction
287326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
288804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            rankedLine = DebugRankedLine("%s: %s" % (self, string))
289804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self._logDebug(rankedLine, level)
290326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
291804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _logDebug(self, rankedLine, level):
292326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
293804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if self.parent:
294804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.parent._logDebug(rankedLine.increasedRank(), level)
295804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
296804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.log(level, str(rankedLine))
297326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
298326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
299326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
300326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
301326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass FromDomElement(Element):
302804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, DomElement):
303804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._initFromDom(DomElement)
304804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().__init__(self.name)
305326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
306326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
307804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _initFromDom(self, DomElement):
308804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.name = DomElement.getAttribute("Name")
309326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
310326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
311326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
312326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass DomElementLocation():
313804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, classConstructor, path=None):
314804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.classConstructor = classConstructor
315804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if path :
316804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.path = path
317804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
318804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.path = []
319326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
320804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.path.append(classConstructor.tag)
321326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
322326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
323326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass DomPopulatedElement(Element):
324804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """Default child populate
325326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
326804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    Look for each dom element with tag specified in self.tag
327804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    and instantiate it with the dom element
328804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """
329804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = []
330326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
331804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def populate(self, dom):
332326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
333804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for childDomElementLocation in self.childClasses :
334326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
335804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("Looking for child %s in path %s" % (
336804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                childDomElementLocation.path[-1], childDomElementLocation.path))
337326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
338804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            for childDomElement in self._findChildFromTagPath(dom, childDomElementLocation.path) :
339326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
340804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                childElement = childDomElementLocation.classConstructor(childDomElement)
341804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                self.addChild(childElement)
342326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
343804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                childElement.populate(childDomElement)
344326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
345804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _findChildFromTagPath(self, dom, path):
346804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if not path :
347804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            yield dom
348804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
349804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Copy list
350804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            path = list(path)
351326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
352804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            tag = path.pop(0)
353326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
354804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Find element with tag
355804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("Going to find elements with tag %s in %s" % (tag, dom))
356804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug(lambda: "Nb of solutions: %s" % len(dom.getElementsByTagName(tag)))
357326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
358804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            for elementByTag in dom.getElementsByTagName(tag) :
359326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
360804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                self.debug("Found element: %s" % elementByTag)
361326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
362804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                # If the same tag is found
363804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                if elementByTag in dom.childNodes :
364326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
365804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    # Yield next level
366804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    for element in self._findChildFromTagPath(elementByTag, path) :
367804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        yield element
368326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
369326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
370326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Rule(Element):
371326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
372804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def usedIfApplicable(self, criteria):
373804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        childApplicability = (child.usedIfApplicable(criteria)
374804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                for child in self.children)
375326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
376804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        isApplicable = self._isApplicable(criteria, childApplicability)
377326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
378804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if isApplicable :
379804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self._incNbUse()
380326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
381804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Rule applicability: %s" % isApplicable)
382804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        assert(isApplicable == True or isApplicable == False)
383326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
384804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return isApplicable
385326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
386326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
387804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _isApplicable(self, criteria, childApplicability):
388804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """Return the rule applicability depending on children applicability.
389cf031996ede8428065b6d7648e34720a1874f5faKevin Rocard
390804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        If at least one child is applicable, return true"""
391804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Lazy evaluation as in the PFW
392804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return all(childApplicability)
393326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
394326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
395326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass CriterionRule(FromDomElement, DomPopulatedElement, Rule):
396804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "SelectionCriterionRule"
397804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = []
398804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    isApplicableOperations = {
399804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "Includes" : lambda criterion, value:     criterion.stateIncludes(value),
400804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "Excludes" : lambda criterion, value: not criterion.stateIncludes(value),
401804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "Is"       : lambda criterion, value:     criterion.stateIs(value),
402804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "IsNot"    : lambda criterion, value: not criterion.stateIs(value)
403804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            }
404326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
405804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _initFromDom(self, DomElement):
406804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.selectionCriterion = DomElement.getAttribute("SelectionCriterion")
407804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.matchesWhen = DomElement.getAttribute("MatchesWhen")
408804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.value = DomElement.getAttribute("Value")
409804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.name = "%s %s %s" % (self.selectionCriterion, self.matchesWhen, self.value)
410326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
411804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        applicableOperationWithoutValue = self.isApplicableOperations[self.matchesWhen]
412804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.isApplicableOperation = lambda criterion: applicableOperationWithoutValue(criterion, self.value)
413326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
414804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _isApplicable(self, criteria, childApplicability):
415326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
416804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return criteria.operationOnChild([self.selectionCriterion],
417804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                self.isApplicableOperation)
418326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
419326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
420326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass CompoundRule(FromDomElement, DomPopulatedElement, Rule):
421804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """CompoundRule can be of type ALL or ANY"""
422804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "CompoundRule"
423804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    # Declare childClasses but define it at first class instantiation
424804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = None
425326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
426804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, dom):
427804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Define childClasses at first class instantiation
428804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if self.childClasses == None :
429804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.childClasses = [DomElementLocation(CriterionRule),
430804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    DomElementLocation(CompoundRule)]
431804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().__init__(dom)
432326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
433804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _initFromDom(self, DomElement):
434326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
435804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        type = DomElement.getAttribute("Type")
436804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.ofTypeAll = {"All" : True, "Any" : False}[type]
437804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.name = type
438326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
439804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _isApplicable(self, criteria, childApplicability):
440804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if self.ofTypeAll :
441804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            applicability = super()._isApplicable(criteria, childApplicability)
442804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else:
443804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Lazy evaluation as in the PFW
444804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            applicability = any(childApplicability)
445326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
446804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return applicability
447326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
448326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass RootRule(DomPopulatedElement, Rule):
449804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "RootRule"
450804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = [DomElementLocation(CompoundRule)]
451326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
452804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def populate(self, dom):
453804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().populate(dom)
454804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Children: %s" % self.children)
455804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # A configuration can only have one or no rule
456804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        assert(len(self.children) <= 1)
457326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
458804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getCoverageDependanceElements(self):
459804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self._getDescendants()
460326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
461326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
462326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass CriteronStates(Element):
463804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """Root of configuration application criterion state"""
464804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "CriterionStates"
465326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
466804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def parentUsed(self, criteria):
467804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """Add criteria to child if not exist, if exist increase it's nbUse"""
468804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._incNbUse()
469326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
470804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        matches = [child for child in self.children if child == criteria]
471326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
472804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        assert(len(matches) <= 1)
473326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
474804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if matches :
475804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("Criteria state has already been encounter")
476804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            currentcriteria = matches[0]
477804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
478804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("Criteria state has never been encounter, saving it")
479804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            currentcriteria = criteria
480804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.addChild(criteria)
481326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
482804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        currentcriteria.parentUsed()
483326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
484326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
485326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
486326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Configuration(FromDomElement, DomPopulatedElement):
487804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "Configuration"
488804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = []
489326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
490804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    class IneligibleConfigurationAppliedError(CustomError):
49197dbd35c75ee252a674aff3d66293d55fc0b35a5Kevin Rocard
492804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        def __init__(self, configuration, criteria):
493804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.configuration = configuration
494804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.criteria = criteria
49597dbd35c75ee252a674aff3d66293d55fc0b35a5Kevin Rocard
496804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        def __str__(self):
49797dbd35c75ee252a674aff3d66293d55fc0b35a5Kevin Rocard
498804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return ("Applying ineligible %s, "
499804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "rule:\n%s\n"
500804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "Criteria current state:\n%s" % (
501804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    self.configuration,
502804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    self.configuration.rootRule.dump(withCoverage=False, withNbUse=False),
503804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    self.criteria.dump(withCoverage=False, withNbUse=False)
504804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    ))
50597dbd35c75ee252a674aff3d66293d55fc0b35a5Kevin Rocard
506804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, DomElement):
507804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().__init__(DomElement)
508326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
509804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.rootRule = RootRule("RootRule")
510804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.addChild(self.rootRule)
511326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
512804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.criteronStates = CriteronStates("CriterionStates")
513804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.addChild(self.criteronStates)
514326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
515804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def populate(self, dom):
516804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Delegate to rootRule
517804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.rootRule.populate(dom)
518326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
519804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getCoverage(self):
520804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Delegate to rootRule
521804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.rootRule._getCoverage()
522326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
523804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def used(self, criteria):
524326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
525804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._incNbUse()
526326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
527804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Propagate use to parents
528804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._tellParentThatChildUsed()
529326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
530804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Propagate to criterion coverage
531804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.criteronStates.parentUsed(criteria.export())
532326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
533804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Propagate to rules
534804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if not self.rootRule.usedIfApplicable(criteria) :
535326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
536804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debug("Applied but rule does not match current "
5373aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard                       "criteria (parent: %s) " % self.parent.name,
538804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    logging.ERROR)
539326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
540804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            raise self.IneligibleConfigurationAppliedError(self, criteria.export())
541326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
542804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _dumpPropagate(self, withCoverage, withNbUse):
543804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Going to ask %s for description" % self.rootRule)
544804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for dumpedDescription in self.rootRule._dumpDescription(
545804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                withCoverage=withCoverage,
546804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                withNbUse=withNbUse) :
547804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            yield dumpedDescription.increasedRank()
548326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
549804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Going to ask %s for description" % self.criteronStates)
550804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for dumpedDescription in self.criteronStates._dumpDescription(
551804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                withCoverage=False,
552804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                withNbUse=withNbUse) :
553804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            yield dumpedDescription.increasedRank()
554326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
555326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
556326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Domain(FromDomElement, DomPopulatedElement):
557804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "ConfigurableDomain"
558804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = [DomElementLocation(Configuration, ["Configurations"])]
559326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
560326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
561326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Domains(DomPopulatedElement):
562804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "Domains"
563804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    childClasses = [DomElementLocation(Domain, ["ConfigurableDomains"])]
564326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
565326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
566326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass RankedLine():
567804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, string,
568804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                stringPrefix="|-- ",
569804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                rankString="|   ",
570804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                linePrefix="",
571804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                lineSuffix="\n"):
572804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.string = string
573804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.rank = 0
574804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.stringPrefix = stringPrefix
575804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.rankString = rankString
576804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.linePrefix = linePrefix
577804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.lineSuffix = lineSuffix
578804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
579804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def increasedRank(self):
580804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.rank += 1
581804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self
582804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
583804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __str__(self):
584804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.linePrefix + \
585804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.rank * self.rankString + \
586804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.stringPrefix + \
587804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.string + \
588804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.lineSuffix
589326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
590326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass DebugRankedLine(RankedLine):
591326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
592804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, string, lineSuffix=""):
593804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().__init__(string,
594804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                stringPrefix="",
595804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                rankString="   ",
596804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                linePrefix="",
597804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                lineSuffix=lineSuffix)
598326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
599326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
600326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass CriterionState(Element):
601804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "CriterionState"
602804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def used(self):
603804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._incNbUse()
604326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
605326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
606326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Criterion(Element):
607804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "Criterion"
608804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    inclusivenessTranslate = {True: "Inclusive", False: "Exclusive"}
6093aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
610804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    class ChangeRequestToNonAccessibleState(CustomError):
611804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        def __init__(self, requestedState, detail):
612804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.requestedState = requestedState
613804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.detail = detail
614556538e2bda03b54bdd82c5813a5286e90a39f67Kevin Rocard
615804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        def __str__(self):
616804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return ("Change request to non accessible state %s. Detail: %s" %
617804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                (self.requestedState, self.detail))
618556538e2bda03b54bdd82c5813a5286e90a39f67Kevin Rocard
619804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, name, isInclusif,
620804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                stateNamesList, currentStateNamesList,
621804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                ignoreIntegrity=False):
622804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().__init__(name)
623804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.isInclusif = isInclusif
624326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
625804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for state in stateNamesList :
626804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.addChild(CriterionState(state))
627326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
628804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.currentState = []
629804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.initStateNamesList = list(currentStateNamesList)
630804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.changeState(self.initStateNamesList, ignoreIntegrity)
631326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
632804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def reset(self):
633804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Set current state as provided at initialisation
634804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.changeState(self.initStateNamesList, ignoreIntegrity=True)
635326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
636804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def changeState(self, subStateNames, ignoreIntegrity=False):
637804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Changing state from: %s to: %s" % (
638804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    list(self._getElementNames(self.currentState)),
639804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    subStateNames))
640326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
641804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if not ignoreIntegrity and not self.isIntegre(subStateNames):
642804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            raise self.ChangeRequestToNonAccessibleState(subStateNames,
643804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                "An exclusive criterion must have a non empty state")
644326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
645804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        newCurrentState = []
646804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for subStateName in subStateNames :
647804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            subState = self.getChildFromName(subStateName)
648804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            subState.used()
649804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            newCurrentState.append(subState)
650326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
651804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.currentState = newCurrentState
652326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
653804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._incNbUse()
654804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self._tellParentThatChildUsed()
655326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
656804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def isIntegre(self, subStateNames):
657804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return self.isInclusif or len(subStateNames) == 1
658556538e2bda03b54bdd82c5813a5286e90a39f67Kevin Rocard
659804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def childUsed(self, child):
660804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.currentState = child
661804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().childUsed(child)
662556538e2bda03b54bdd82c5813a5286e90a39f67Kevin Rocard
663804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def export(self):
664804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        subStateNames = self._getElementNames(self.currentState)
665804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return Criterion(self.name, self.isInclusif, subStateNames, subStateNames,
666804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            ignoreIntegrity=True)
667326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
668804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def stateIncludes(self, subStateName):
669804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        subStateCurrentNames = list(self._getElementNames(self.currentState))
670326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
671804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Testing if %s is included in %s" % (subStateName, subStateCurrentNames))
672326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
673804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        isIncluded = subStateName in subStateCurrentNames
674804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("IsIncluded: %s" % isIncluded)
675326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
676804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return isIncluded
677326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
678326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
679804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def stateIs(self, subStateNames):
680804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if len(self.currentState) != 1 :
681804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return False
682804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
683804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return self.stateIncludes(subStateNames)
684326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
685804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _getXMLAttributes(self):
686804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        attributes = super()._getXMLAttributes()
687804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        attributes["Type"] = self.inclusivenessTranslate[self.isInclusif]
688804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return attributes
6893aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
690326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
691326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Criteria(Element):
692804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "Criteria"
693326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
694804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    class DuplicatedCriterionError(DuplicatedChildError):
695804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        pass
696d077c557e360615a67d97ee249260a1ef7919f09Kevin Rocard
697804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def export(self):
698804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.debug("Exporting criteria")
699804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        assert(self.children)
700326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
701804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        exported = Criteria(self.name)
702804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        for child in self.children :
703804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            exported.addChild(child.export())
704804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return exported
705326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
706804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def addChild(self, child):
707804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if child in self.children:
708804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            raise self.DuplicatedCriterionError(self, child)
709804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().addChild(child)
7103aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
7113aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocardclass ConfigAppliedWithoutCriteriaError(CustomError):
712804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, configurationName, domainName):
713804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.configurationName = configurationName
714804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.domainName = domainName
715804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __str__(self):
716804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return ('Applying configuration "%s" from domain "%s" before declaring criteria' %
717804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                (self.configurationName, self.domainName))
718326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
719326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass ParsePFWlog():
720804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    MATCH = "match"
721804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    ACTION = "action"
722804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
723ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner    class ChangeRequestOnUnknownCriterion(CustomError):
724ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner        def __init__(self, criterion):
725ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner            self.criterion = criterion
726ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner
727ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner        def __str__(self):
728ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner            return ("Change request on an unknown criterion %s." %
729ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner                self.criterion)
730ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner
731804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, domains, criteria, ErrorsToIgnore=()):
732804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
733804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.domains = domains;
734804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.criteria = criteria;
735804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.ErrorsToIgnore = ErrorsToIgnore
736326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
737804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        configApplicationRegext = r""".*Applying configuration "(.*)" from domain "([^"]*)"""
738804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        matchConfigApplicationLine = re.compile(configApplicationRegext).match
739804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
740804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        criterionCreationRegext = ", ".join([
741804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    r""".*Criterion name: (.*)""",
742804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    r"""type kind: (.*)""",
743804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    r"""current state: (.*)""",
744804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    r"""states: {(.*)}"""
745804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                ])
746804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        matchCriterionCreationLine = re.compile(criterionCreationRegext).match
747326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
748804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        changingCriterionRegext = r""".*Selection criterion changed event: Criterion name: (.*), current state: ([^\n\r]*)"""
749804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        matchChangingCriterionLine = re.compile(changingCriterionRegext).match
750326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
751804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.lineLogTypes = [
752804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    {
753804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self.MATCH: matchConfigApplicationLine,
754804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self.ACTION: self._configApplication
755804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    }, {
756804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self.MATCH: matchCriterionCreationLine,
757804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self.ACTION: self._criterionCreation
758804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    }, {
759804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self.MATCH: matchChangingCriterionLine,
760804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        self.ACTION: self._changingCriterion
761804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    }
762804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                ]
763326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
764804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    @staticmethod
765804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _formatCriterionList(liststring, separator):
766804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        list = liststring.split(separator)
767804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if len(list) == 1 and list[0] == "<none>":
768804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            list = []
769804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return list
770804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
771804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _criterionCreation(self, matchCriterionCreation):
772804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Unpack
773804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        criterionName, criterionType, currentCriterionStates, criterionStates = matchCriterionCreation.group(1, 2, 3, 4)
774326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
775804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        criterionStateList = self._formatCriterionList(criterionStates, ", ")
776326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
777804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        criterionIsInclusif = {"exclusive" : False, "inclusive" : True}[criterionType]
778326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
779804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        currentcriterionStateList = self._formatCriterionList(currentCriterionStates, "|")
780326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
781804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        logger.info("Creating criterion: " + criterionName +
782804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    " (" + criterionType + ") " +
783804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    " with current state: " + str(currentcriterionStateList) +
784804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    ", possible states:" + str(criterionStateList))
785326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
786804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        try:
787804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.criteria.addChild(Criterion(
788804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    criterionName,
789804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    criterionIsInclusif,
790804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    criterionStateList,
791804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    currentcriterionStateList
792804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                ))
793804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        except self.criteria.DuplicatedCriterionError as ex:
794804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.debug(ex)
795804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.warning("Reseting criterion %s. Did you reset the PFW ?" % criterionName)
796804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.criteria.operationOnChild(
797804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                [criterionName],
798804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                lambda criterion: criterion.reset()
799804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            )
800ea87422e14735633cb8e28de114a18e22eaa82fbKevin Rocard
801ea87422e14735633cb8e28de114a18e22eaa82fbKevin Rocard
802326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
803804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _changingCriterion(self, matchChangingCriterion):
804804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Unpack
805804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        criterionName, newCriterionSubStateNames = matchChangingCriterion.group(1, 2)
806326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
807804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        newCriterionState = self._formatCriterionList(newCriterionSubStateNames, "|")
808326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
809804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        logger.info("Changing criterion %s to %s" % (criterionName , newCriterionState))
8103aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
811804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        path = [criterionName]
812804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        changeCriterionOperation = lambda criterion : criterion.changeState(newCriterionState)
813ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner        try:
814ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner            self.criteria.operationOnChild(path, changeCriterionOperation)
815ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner        except ChildNotFoundError:
816ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner            raise self.ChangeRequestOnUnknownCriterion(criterionName)
817326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
818804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _configApplication(self, matchConfig):
819804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Unpack
820804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        configurationName, domainName = matchConfig.group(1, 2)
8213aa0db4be952157c2842b91a1606cc0edac9e63dKevin Rocard
822804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Check that at least one criterion exist
823804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if not self.criteria.hasChildren() :
824804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.error("Applying configuration before declaring criteria")
825804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.info("Is the log starting at PFW boot ?")
826804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            raise ConfigAppliedWithoutCriteriaError(configurationName, domainName)
827326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
828804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Change criterion state
829804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        path = [domainName, configurationName]
830804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        usedOperation = lambda element : element.used(self.criteria)
831326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
832804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        logger.info("Applying configuration %s from domain %s" % (
833804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                configurationName, domainName))
834804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
835804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.domains.operationOnChild(path, usedOperation)
836326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
837326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
838804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def _digest(self, lineLogType, lineLog):
8399050c81c8f9a63aa95e7230705e00585bd0ecec5Kevin Rocard
840804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        match = lineLogType[self.MATCH](lineLog)
841804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        if match :
842804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            lineLogType[self.ACTION](match)
843804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            return True
844804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        return False
845326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
8469050c81c8f9a63aa95e7230705e00585bd0ecec5Kevin Rocard
847804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def parsePFWlog(self, lines):
8486bd9fcf524e77a1ca7469d6d91c58afd242d388aKevin Rocard        for lineNb, lineLog in enumerate(lines, 1): # line number starts at 1
849326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
850804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.debug("Parsing line :%s" % lineLog.rstrip())
851326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
852804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            digested = (self._digest(lineLogType, lineLog)
853804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    for lineLogType in self.lineLogTypes)
854326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
855804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            try:
856804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                success = any(digested)
8579050c81c8f9a63aa95e7230705e00585bd0ecec5Kevin Rocard
858804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Catch some exception in order to print the current parsing line,
859804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # then raise the exception again if not continue of error
860804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            except CustomError as ex:
861804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                logger.error('Error raised while parsing line %s: "%s"' %
862804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                            (lineNb, repr(lineLog)))
8639050c81c8f9a63aa95e7230705e00585bd0ecec5Kevin Rocard
864804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                # If exception is a subclass of ErrorsToIgnore, log it and continue
865804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                # otherwise raise it again.
866804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                if not issubclass(type(ex), self.ErrorsToIgnore):
867804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    raise ex
868804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                else:
869804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    logger.error('Ignoring exception:"%s", '
870804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                                'can not guarantee database integrity' % ex)
871804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            else:
872804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                if not success:
873804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    logger.debug("Line does not match, dropped")
874326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
875326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
876326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass Root(Element):
877804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    tag = "CoverageReport"
878804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self, name, dom):
879804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        super().__init__(name)
880804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Create domain tree
881804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.domains = Domains("Domains")
882804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.domains.populate(dom)
883804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.addChild(self.domains)
884804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        # Create criterion list
885804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.criteria = Criteria("CriterionRoot")
886804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        self.addChild(self.criteria)
887804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
888804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def exportToXML(self):
889804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        """Export tree to an xml document"""
890804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        impl = xml.dom.minidom.getDOMImplementation()
891794fea64ed11b6c2b2604c3a0daa374c885f09d6Kevin Rocard        document = impl.createDocument(namespaceURI=None, qualifiedName=self.tag, doctype=None)
892794fea64ed11b6c2b2604c3a0daa374c885f09d6Kevin Rocard        super().exportToXML(document, document.documentElement)
893804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
894794fea64ed11b6c2b2604c3a0daa374c885f09d6Kevin Rocard        return document
895326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
896326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard# ============================
897326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard# Command line argument parser
898326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard# ============================
899326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
900326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
901326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardclass ArgumentParser:
902804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """class that parse command line arguments with argparse library
903804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
904804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    Result of parsing are the class attributes.
905804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """
906804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    levelTranslate = [logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG]
907804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
908804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    def __init__(self):
909804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
910804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        try:
911804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # As argparse is only in the stdlib since python 3.2,
912804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # testing its availability
913804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            import argparse
914804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
915804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        except ImportError:
916804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.warning("Unable to import argparse "
917804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                           "(parser for command-line options and arguments), "
918804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                           "using default argument values:")
919804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
920804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.warning(" - InputFile: stdin")
921804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.inputFile = sys.stdin
922804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
923804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.warning(" - OutputFile: stdout")
924804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.outputFile = sys.stdout
925804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
926804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            try:
927804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                self.domainsFile = sys.argv[1]
928804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            except IndexError as ex:
929804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                logger.fatal("No domain file provided (first argument)")
930804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                raise ex
931804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            else:
932804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                logger.warning(" - Domain file: " + self.domainsFile)
933804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
934804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.warning(" - Output format: xml")
935804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.XMLreport = True
936804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
937804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logger.warning(" - Debug level: error")
938804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debugLevel = logging.ERROR
939804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        else :
940804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
941804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser = argparse.ArgumentParser(description='Generate PFW report')
942804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
943804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser.add_argument(
944804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        'domainsFile',
945804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        type=argparse.FileType('r'),
946804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="the PFW domain XML file"
947804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
948804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser.add_argument(
949804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        'pfwlog', nargs='?',
950804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        type=argparse.FileType('r'), default=sys.stdin,
951804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="the PFW log file, default stdin"
952804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
953804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser.add_argument(
954804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        '-o', '--output',
955804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        dest="outputFile",
956804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        type=argparse.FileType('w'), default=sys.stdout,
957804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="the coverage report output file, default stdout"
958804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
959804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser.add_argument(
960804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        '-v', '--verbose',
961804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        dest="debugLevel", default=0,
962804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        action='count',
963804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="print debug warnings from warning (default) to debug (-vv)"
964804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
965804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
966804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            outputFormatGroupe = myArgParser.add_mutually_exclusive_group(required=False)
967804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
968804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            outputFormatGroupe.add_argument(
969804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        '--xml',
970804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        dest="xmlFlag",
971804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        action='store_true',
972804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help=" XML coverage output report"
973804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
974804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            outputFormatGroupe.add_argument(
975804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        '--raw',
976804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        dest="rawFlag",
977804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        action='store_true',
978804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="raw coverage output report"
979804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
980804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
981804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser.add_argument(
982ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner                        '--ignore-unknown-criterion',
983ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner                        dest="unknwonCriterionFlag",
984ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner                        action='store_true',
985ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner                        help="ignore unknown criterion"
986ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner                    )
987ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner
988ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner            myArgParser.add_argument(
989804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        '--ignore-incoherent-criterion-state',
990804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        dest="incoherentCriterionFlag",
991804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        action='store_true',
992804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="ignore criterion transition to incoherent state"
993804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
994804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
995804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            myArgParser.add_argument(
996804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        '--ignore-ineligible-configuration-application',
997804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        dest="ineligibleConfigurationApplicationFlag",
998804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        action='store_true',
999804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        help="ignore application of configuration with a false rule "
1000804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                        "(not applicable configuration)"
1001804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                    )
1002804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
1003804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Process command line arguments
1004804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            options = myArgParser.parse_args()
1005804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
1006804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Mapping to attributes
1007804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.inputFile = options.pfwlog
1008804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.outputFile = options.outputFile
1009804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.domainsFile = options.domainsFile
1010804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
1011804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Output report in xml if flag not set
1012804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.XMLreport = not options.rawFlag
1013804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
1014804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Setting logger level
1015804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            levelCapped = min(options.debugLevel, len(self.levelTranslate) - 1)
1016804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.debugLevel = self.levelTranslate[levelCapped]
1017804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
1018804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            # Setting ignore options
1019804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            errorToIgnore = []
1020804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            if options.ineligibleConfigurationApplicationFlag :
1021804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard                errorToIgnore.append(Configuration.IneligibleConfigurationAppliedError)
1022804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
1023804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            if options.incoherentCriterionFlag:
102412d9c82b31650d307e3834e435e1f5f54e3832d0David Wagner                errorToIgnore.append(Criterion.ChangeRequestToNonAccessibleState)
1025ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner
1026ace56ce368fa42b3eb496382e7387a47fff5f0a6David Wagner            if options.unknwonCriterionFlag:
102712d9c82b31650d307e3834e435e1f5f54e3832d0David Wagner                errorToIgnore.append(ParsePFWlog.ChangeRequestOnUnknownCriterion)
1028804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard
1029804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            self.errorToIgnore = tuple(errorToIgnore)
10309050c81c8f9a63aa95e7230705e00585bd0ecec5Kevin Rocard
1031326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1032326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1033326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocarddef main():
1034326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1035804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    errorDuringLogParsing = -1
1036804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    errorDuringArgumentParsing = 1
103753493b2f22bc2d491f7607672e1d07e588677efaKevin Rocard
1038804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    try:
1039804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        commandLineArguments = ArgumentParser()
1040804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    except LookupError as ex:
1041804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        logger.error("Error during argument parsing")
1042804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        logger.debug(str(ex))
1043804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        sys.exit(errorDuringArgumentParsing)
1044326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1045804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    # Setting logger level
1046804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    logger.setLevel(commandLineArguments.debugLevel)
1047804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    logger.info("Log level set to: %s" %
1048804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            logging.getLevelName(commandLineArguments.debugLevel))
1049326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1050804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    # Create tree from XML
1051804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    dom = xml.dom.minidom.parse(commandLineArguments.domainsFile)
1052326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1053804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    # Create element tree
1054804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    root = Root("DomainCoverage", dom)
1055326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1056804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    # Parse PFW events
1057804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    parser = ParsePFWlog(root.domains, root.criteria, commandLineArguments.errorToIgnore)
105853493b2f22bc2d491f7607672e1d07e588677efaKevin Rocard
1059804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    try:
1060804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        parser.parsePFWlog(commandLineArguments.inputFile.readlines())
1061804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    except CustomError as ex:
1062804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        logger.fatal("Error during parsing log file %s: %s" %
1063804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard            (commandLineArguments.inputFile, ex))
1064804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        sys.exit(errorDuringLogParsing)
1065326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1066804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    # Output report
1067804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    outputFile = commandLineArguments.outputFile
1068326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1069804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    if not commandLineArguments.XMLreport :
1070804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        outputFile.write("%s\n" % root.dump(withCoverage=True, withNbUse=True))
1071804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    else :
1072804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard        outputFile.write(root.exportToXML().toprettyxml())
1073326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1074326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1075326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocardif __name__ == "__main__" :
1076804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    """ Execute main if the python interpreter is running this module as the main program """
1077804e064dcd02b87e04b9a189422cc14205e8125cKevin Rocard    main()
1078326e39e42a801bff4b61655ebc0e3ff759c208ffKevin Rocard
1079