Commit 63372f7a authored by VIGNET Pierre's avatar VIGNET Pierre
Browse files

Proper way to code a project: Doc + perfs => Model load is reduced by 30: Have fun.

parent 47060c66
......@@ -45,8 +45,10 @@
"""
Classes for representing a guarded transition model
"""
from __future__ import unicode_literals
from __future__ import print_function
from math import sqrt
from collections import defaultdict
from cadbiom.antlr3 import ANTLRStringStream, ANTLRFileStream, CommonTokenStream
from condexpLexer import condexpLexer
......@@ -54,12 +56,13 @@ from condexpParser import condexpParser
from cadbiom.models.guard_transitions.translators.cadlangLexer import cadlangLexer
from cadbiom.models.guard_transitions.translators.cadlangParser import cadlangParser
from cadbiom import commons as cm
# number of simple nodes before we draw macros as simple nodes
MAX_SIZE_MACRO = 50
MAX_SIZE = 2000 # max number of nodes we draw
LOGGER = cm.logger()
class ChartModelException(Exception):
"""
......@@ -967,7 +970,28 @@ class CMacroNode(CSimpleNode):
self.hloc = height
self.sub_nodes = []
# list<list<CTransitions>> Sublists: transitions with common extremities
self.transitions = []
# self.transitions = []
# See the property 'transitions' to find a wrapper for old code
self.new_transitions = defaultdict(list)
@property
def transitions(self):
"""Compatibility code
.. note:: The old API uses this attribute as a list<list<CTransitions>>.
Sublists are transitions with common extremities.
:return: An iterator over the values of self.new_transitions.
Similar to: <list <list <CTransitions>>>
:rtype: <dictionary-valueiterator>
"""
return self.new_transitions.itervalues()
@transitions.setter
def transitions(self, _):
"""Block further modifications of the old attribute transitions
.. note:: Please modify self.new_transitions instead.
"""
raise Exception("NOT AUTHORIZED! Please modify new_transitions instead")
def _find_in_subnodes(self, node):
"""
......@@ -1147,48 +1171,91 @@ class CMacroNode(CSimpleNode):
self.model.notify()
return nnode
def add_transition(self, ori, targ):
"""
Add a transition
def add_transition(self, ori, ext):
"""Add a transition to the model
@param ori: a simple node
@param targ: a simple node
.. note:: Reflexive transitions are not authorized.
Duplications of transitions are not authorized.
BUT! Not exception is raised in these cases. Have fun <3
:param ori: a simple node
:param ext: a simple node
:return: A new transition or None if the couple of nodes was not valid.
:rtype: <CTransition> or None
"""
# transition between a node and itself are forbidden
if ori == targ:
return None
# new code: at most one transition in each direction
add = False
for tlist in self.transitions:
if len(tlist) == 1:
trans = tlist[0]
if (trans.ori == ori) and (trans.ext == targ):
return None
if (trans.ori == targ) and (trans.ext == ori):
ntrans = CTransition(ori, targ)
ntrans.macro_node = self
tlist.append(ntrans)
add = True
else: # len(tl) == 2
trans = tlist[0]
if (trans.ori == ori) and (trans.ext == targ):
return None
trans = tlist[1]
if (trans.ori == ori) and (trans.ext == targ):
return None
if not add:
ntrans = CTransition(ori, targ)
ntrans.macro_node = self
self.transitions.append([ntrans])
if ntrans:
ori.outgoing_trans.append(ntrans)
targ.incoming_trans.append(ntrans)
self.model.transition_list.append(ntrans)
self.model.modified = True
self.model.notify()
return ntrans
if ori == ext:
LOGGER.warning(
"Reflexive transition: " + ori.name + ' -> ' + ext.name +
"This transition will not be taken into account.\n"
"Please review your model or use a PermanentNode instead"
)
return
# Search the current couple of nodes in all the transitions
# self.new_transitions is a defaultdict with frozensets as keys
# and lists as values. Each value has at most 2 transitions
# (one in each direction). Each value is called "transitions group"
# later in the code.
nodes_couple = frozenset((ori.name, ext.name))
transitions_group = self.new_transitions[nodes_couple]
if len(transitions_group) == 0:
# New transition: the couple was nod found before
return self.build_new_transition_to_nodes(
transitions_group, ori, ext
)
elif len(transitions_group) == 1:
# Duplication of a transition ?
trans = transitions_group[0]
if not ((trans.ori == ori) and (trans.ext == ext)):
# Incomplete transition: add the other one
return self.build_new_transition_to_nodes(
transitions_group, ori, ext
)
else:
LOGGER.warning(
"Duplicated transition: " + ori.name + ' -> ' + ext.name +
"\nYou should review your model. Only the first transition "
"will be taken into account."
)
return
else:
LOGGER.error("More than one transition for a couple of nodes")
LOGGER.error("Current transition:" + ori.name + ' -> ' + ext.name)
raise Exception(
"ERROR: More than one transition for a couple of nodes"
)
LOGGER.error("Hi! You should never have reached this part.")
LOGGER.error("Current transition:" + ori.name + ' -> ' + ext.name)
raise Exception("Hi! You should never have reached this part.")
def build_new_transition_to_nodes(self, transitions_group, ori, ext):
"""Handle a new transition: build and attach it to the model
:param arg1: List of transitions that concern the given couple of nodes
The list should not exceed 2 elements (see add_transition())
:param arg2: Node
:param arg3: Node
:type arg1: <list <CTransition>>
:type arg2: <Node>
:type arg3: <Node>
"""
# Build new transition
new_transition = CTransition(ori, ext)
new_transition.macro_node = self
# Append the new transition to the internal structure that groups
# couple of transitions with same nodes
transitions_group.append(new_transition)
# Append new transition to the concerned ori and ext nodes
new_transition.ori.outgoing_trans.append(new_transition)
new_transition.ext.incoming_trans.append(new_transition)
# Append new transition to the model
self.model.transition_list.append(new_transition)
self.model.modified = True
self.model.notify()
return new_transition
def add_start_node(self, xcoord, ycoord, name = None):
"""
......
......@@ -314,6 +314,12 @@ class MakeHandler(ContentHandler):
def startElement(self, name, att):
"""Signal the start of an element
.. notes:: Nodes have to be at the top of the model (Before transitions)
Transitions do not allow reflexive ones
(as it could be said in the doc);
Duplication of transitions are not authorized but only print a
warning in the shell (they are not taken into account)
:param arg1: Contains the raw XML 1.0 name of the element.
:param arg2: Holds an object of the Attributes interface.
:type arg1: <str>
......@@ -321,48 +327,17 @@ class MakeHandler(ContentHandler):
"""
#print(att.getNames())
if name == "model":
if not self.model:
self.model = ChartModel(att.get('name',''))
# Root is a virtual macronode on top of the hierarchy.
# A model can be a list of hierarchy grouped under this node.
root = self.model.get_root()
self.pile_node.append(root)
self.top_pile = root
self.init_node_functions()
new_dict = dict()
self.pile_dict.append(new_dict)
self.node_dict = new_dict
elif name in self.nodes_types:
if name in self.nodes_types:
# TODO: Uniformization of API in CMacroNode() class;
# the attribute 'name' should be at the same last position...
element_name = att.get('name', '').encode('ascii')
self.current_element = self.add_node_functions[name](
name=element_name,
xcoord=float(att.get('xloc', '')),
ycoord=float(att.get('yloc', '')),
xcoord=float(att.get('xloc', '0')),
ycoord=float(att.get('yloc', '0')),
)
self.node_dict[element_name] = self.current_element
elif name == "CMacroNode":
name = att.get('name', '').encode('ascii')
xloc = float(att.get('xloc', '') )
yloc = float(att.get('yloc', '') )
wloc = float(att.get('wloc', '') )
hloc = float(att.get('hloc', '') )
node = self.top_pile.add_macro_subnode(name, xloc, yloc,
wloc, hloc)
self.node_dict[name] = node
self.pile_node.append(node)
# symbol table put on stack to preserve macro scope for inputs
new_node_dict = dict()
self.pile_dict.append(new_node_dict)
self.top_pile = node
self.node_dict = new_node_dict
elif name == "transition":
name = att.get('name', '').encode('ascii')
ori = att.get('ori', '')
......@@ -372,13 +347,13 @@ class MakeHandler(ContentHandler):
action = att.get('action', '')
fact_ids_text = att.get('fact_ids','')[1:-1]
if len(fact_ids_text) > 0:
fact_ids_split = fact_ids_text.split(',')
fact_ids = []
for fid in fact_ids_split:
fact_ids.append(int(fid))
fact_ids = [int(id) for id in fact_ids_text.split(',')]
else:
fact_ids = []
# Get nodes involved in the transition
# If not present, raise an exception
# => nodes have to be at the top of the model
try:
node_ori = self.node_dict[ori]
node_ext = self.node_dict[ext]
......@@ -388,17 +363,51 @@ class MakeHandler(ContentHandler):
print(exc)
self.current_element = self.top_pile.add_transition(node_ori, node_ext)
# the transition may not be created (origin = ext for example)
# The transition may not be created (origin = ext for example)
# /!\ Transitions do not allow reflexive ones
# (as it could be said in the doc)
# Duplication of transitions are not authorized but only print a
# warning in the shell (they are not taken into account)
if self.current_element:
self.current_element.set_event(event)
self.current_element.set_condition(condition)
self.current_element.set_action(action)
self.current_element.fact_ids = fact_ids
elif name == "CMacroNode":
name = att.get('name', '').encode('ascii')
xloc = float(att.get('xloc', '0') )
yloc = float(att.get('yloc', '0') )
wloc = float(att.get('wloc', '5') )
hloc = float(att.get('hloc', '5') )
node = self.top_pile.add_macro_subnode(name, xloc, yloc,
wloc, hloc)
self.node_dict[name] = node
self.pile_node.append(node)
# symbol table put on stack to preserve macro scope for inputs
new_node_dict = dict()
self.pile_dict.append(new_node_dict)
self.top_pile = node
self.node_dict = new_node_dict
elif name == 'constraints':
self.in_constraints = True
self.constraints = ""
elif name == "model":
if not self.model:
self.model = ChartModel(att.get('name',''))
# Root is a virtual macronode on top of the hierarchy.
# A model can be a list of hierarchy grouped under this node.
root = self.model.get_root()
self.pile_node.append(root)
self.top_pile = root
self.init_node_functions()
new_dict = dict()
self.pile_dict.append(new_dict)
self.node_dict = new_dict
def characters(self, chr):
"""Receive notification of character data.
......@@ -427,21 +436,22 @@ class MakeHandler(ContentHandler):
to load notes (inner text of xml object).
"""
if name == 'CMacroNode':
if name == 'transition' or name in self.nodes_types:
# Close the current node or transition opened in startElement()
self.current_element = None
elif name == 'CMacroNode':
#self.top_pile = self.pile_node.pop()
self.pile_node.remove(self.top_pile)
self.top_pile = self.pile_node[-1]
#self.node_dict = self.pile_dict.pop()
self.pile_dict.remove(self.node_dict)
self.node_dict = self.pile_dict[-1]
elif name == 'constraints':
self.in_constraints = False
self.model.constraints = self.constraints + '\n'
elif name == 'transition' or name in self.nodes_types:
# Close the current node or transition opened in startElement()
self.current_element = None
#elif name == 'model':
# print(len([e for e in self.top_pile.transitions]))
# print(len(self.top_pile.new_transitions))
class MakeModelFromXmlFile:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment