Example #1
0
def Main():
    cgiEnv = lib_common.CgiEnv()

    grph = cgiEnv.GetGraph()

    # Not really useful.
    grph.add((lib_common.nodeMachine, pc.property_hostname,
              lib_common.NodeLiteral(lib_util.currentHostname)))

    mapToProc = {}

    for proc in CIM_Process.ProcessIter():

        # TODO: Instead, should test psutil version !!!
        try:
            FunctionProcess(mapToProc, proc)
        except CIM_Process.AccessDenied:
            pass
        except Exception:
            lib_common.ErrorMessageHtml("Unexpected error:" +
                                        str(sys.exc_info()[0]))
    # sys.stderr.write( "Leaving processes enumeration\n" )

    addedProcs = {}

    # Now display only memory maps with more than one process linked to it.
    for mapPath, procLst in lib_util.six_iteritems(mapToProc):
        if len(procLst) <= 0:
            continue

        uriMemMap = lib_common.gUriGen.MemMapUri(mapPath)

        for pid in procLst:
            try:
                nodeProcess = addedProcs[pid]
            except KeyError:
                nodeProcess = lib_common.gUriGen.PidUri(pid)
                addedProcs[pid] = nodeProcess

            grph.add((nodeProcess, pc.property_memmap, uriMemMap))
    # sys.stderr.write( "Leaving second maps enumeration\n" )

    # TODO: They could also be displayed based on the hierarchy of their
    # associated file in the directory tree.

    for pid, nodeProcess in lib_util.six_iteritems(addedProcs):
        grph.add((nodeProcess, pc.property_pid, lib_common.NodeLiteral(pid)))

    # TODO: Petit bug: Ca duplique les memmap. Forcement, l'affichage en tables
    # suppose que c'est un arbre. Mais c'est plus rapide et plus clair.
    # cgiEnv.OutCgiRdf("",[pc.property_memmap])
    cgiEnv.OutCgiRdf("LAYOUT_SPLINE")
Example #2
0
def CreateObjs(grph,rootNode,directoryName,objectsByLocation,paramExplodeClasses):
	DEBUG("directoryName=%s num=%d", directoryName, len(objectsByLocation))

	for (locationFile, v1) in lib_util.six_iteritems(objectsByLocation):
		DEBUG("locationFile=%s",locationFile)

		# TODO: Eventuellement exploser selon les sous-directorys
		nodeFile = lib_common.gUriGen.FileUri( locationFile )
		grph.add( ( rootNode, pc.property_directory, nodeFile ) )

		for (compounddefKind, v2) in v1.items():
			#sys.stderr.write("compounddefKind=%s\n"%compounddefKind)
			for (compoundName, v3) in v2.items():
				#sys.stderr.write("compoundName=%s\n"%compoundName)
				for (memberKind, v4) in v3.items():
					#sys.stderr.write("memberKind=%s\n"%memberKind)
					for (memberDefinition, listTypes) in v4.items():
						if memberKind == "function":
							if( len(listTypes) > 1 ):
								concatTypes = ",".join(listTypes[1:])
							else:
								concatTypes = ""
							# funcName = listTypes[0] + " " + memberName + "(" + concatTypes + ")"
							funcName = memberDefinition + "(" + concatTypes + ")"
							#nodeFunction = lib_common.gUriGen.SymbolUri( funcName, locationFile )
							#if nodeFile:
							#	grph.add( ( nodeFile, pc.property_member, nodeFunction ) )
							DisplayDefinition(grph,nodeFile,locationFile,funcName,paramExplodeClasses)
						elif memberKind == "variable":
							DisplayDefinition(grph,nodeFile,locationFile,memberDefinition,paramExplodeClasses)
							# nodeVariable = lib_common.gUriGen.SymbolUri( memberName, locationFile )
							#nodeVariable = lib_common.gUriGen.SymbolUri( memberDefinition, locationFile )
							#if nodeFile:
							#	grph.add( ( nodeFile, pc.property_member, nodeVariable ) )
	return
Example #3
0
def AddCIMClasses(grph, rootNode, entity_host, nameSpace, className,
                  entity_id):
    # Maybe some of these servers are not able to display anything about this object.

    pairNameNodeWbem = None
    if wbemOk:
        if lib_wbem.ValidClassWbem(entity_host, className):
            pairNameNodeWbem = CreateWbemNode(grph, rootNode, entity_host,
                                              nameSpace, className, entity_id)

    pairNameNodeWmi = None
    if lib_wmi.ValidClassWmi(className):
        pairNameNodeWmi = CreateWmiNode(grph, rootNode, entity_host, nameSpace,
                                        className, entity_id)

    # Match the two inheritance trees.
    if pairNameNodeWbem and pairNameNodeWmi:
        for (baseClsNam, nodeWbem) in lib_util.six_iteritems(pairNameNodeWbem):
            try:
                nodeWmi = pairNameNodeWmi[baseClsNam]
            except KeyError:
                continue

            nodeClsAll = lib_util.EntityClassNode(baseClsNam, nameSpace,
                                                  entity_host, "WBEM ou WMI")
            grph.add((nodeClsAll, pc.property_wbem_data, nodeWbem))
            grph.add((nodeClsAll, pc.property_wmi_data, nodeWmi))
Example #4
0
def AddCIMClasses(grph, root_node, entity_host, name_space, class_name,
                  entity_id):
    """entity_type = "CIM_Process", "Win32_Service" etc...
    This might happen at an intermediary level, with inheritance (To be implemented).
    Maybe some of these servers are not able to display anything about this object."""

    pair_name_node_wbem = None
    if wbem_ok:
        if lib_wbem.ValidClassWbem(class_name):
            pair_name_node_wbem = _create_wbem_node(grph, root_node,
                                                    entity_host, name_space,
                                                    class_name, entity_id)

    pair_name_node_wmi = None
    if lib_wmi.ValidClassWmi(class_name):
        pair_name_node_wmi = _create_wmi_node(grph, root_node, entity_host,
                                              name_space, class_name,
                                              entity_id)

    # Match the two inheritance trees.
    if pair_name_node_wbem and pair_name_node_wmi:
        for base_cls_nam, node_wbem in lib_util.six_iteritems(
                pair_name_node_wbem):
            try:
                nodeWmi = pair_name_node_wmi[base_cls_nam]
            except KeyError:
                continue

            node_cls_all = lib_util.EntityClassNode(base_cls_nam, name_space,
                                                    entity_host, "WBEM ou WMI")
            grph.add((node_cls_all, pc.property_wbem_data, node_wbem))
            grph.add((node_cls_all, pc.property_wmi_data, nodeWmi))
Example #5
0
def Main():
    cgiEnv = lib_common.ScriptEnvironment()

    grph = cgiEnv.GetGraph()

    grph.add((lib_common.nodeMachine, pc.property_hostname,
              lib_util.NodeLiteral(lib_util.currentHostname)))

    # This is a dictionary of memory-mapped files to the processes using them.
    map_to_proc = {}

    for proc in psutil.process_iter():
        try:
            # The pid is added to the list of each map used by this process.
            # New maps are added, and also the pid is added to existing of new maps.
            function_process(map_to_proc, proc)
        except CIM_Process.AccessDenied:
            # Most memory maps and processes cannot be access for security reasons.
            pass
        except Exception as exc:
            lib_common.ErrorMessageHtml("Unexpected error:" + exc)

    # This maps the pid to its rdf node.
    added_procs = {}

    # Now display only memory maps with more than one process linked to it.
    for map_path, proc_lst in lib_util.six_iteritems(map_to_proc):
        if len(proc_lst) <= 0:
            continue

        uri_mem_map = lib_uris.gUriGen.MemMapUri(map_path)

        for pid in proc_lst:
            try:
                node_process = added_procs[pid]
            except KeyError:
                node_process = lib_uris.gUriGen.PidUri(pid)
                added_procs[pid] = node_process

            grph.add((node_process, pc.property_memmap, uri_mem_map))

    # TODO: They could also be displayed based on the hierarchy of their associated file in the directory tree.

    for pid, node_process in lib_util.six_iteritems(added_procs):
        grph.add((node_process, pc.property_pid, lib_util.NodeLiteral(pid)))

    cgiEnv.OutCgiRdf("LAYOUT_SPLINE")
Example #6
0
def Rdf2Dot(grph, logfil, stream, CollapsedProperties):
    fieldsSet = collections.defaultdict(list)

    # This maps RDFLIB nodes to DOT label names.
    dictRdf2Dot = {}

    # This returns the DOT label of a RDFLIB, and creates a new one if necessary.
    def RdfNodeToDotLabel(x):
        try:
            return dictRdf2Dot[x]
        except KeyError:
            nodelabel = "nd_%d" % len(dictRdf2Dot)
            dictRdf2Dot[x] = nodelabel
            return nodelabel

    # The QName is an abbreviation of URI reference with the namespace function for XML.
    # Edge label.
    # Transforms "http://primhillcomputers.com/ontologies/ppid" into "ppid"
    # TODO: Beware, a CGI parameter might be there. CGIPROP
    def qname(x, grph):
        try:
            q = grph.compute_qname(x)
            # q[0] is the shortened namespace "ns"
            # Could return q[0] + ":" + q[2]
            return q[2]
        except:
            return x
        # Nothing really interesting at the moment, just hardcodes.
        return lib_properties.prop_color(prop)

    # The input is any Python object.
    # This returns a simple object which can be transformed into a string.
    # If the input is a container, it returns a HTML table.
    def FormatElementAux(val, depth=0):
        if val is None:
            return ""

        try:
            int(val)
            return val
        except:
            pass

        try:
            float(val)
            return val
        except:
            pass

        if isinstance(val, dict):
            subTable = ""
            # TODO: Consider using six.iteritems.
            for subKey, subVal in val.items():
                subTd = FormatPair(subKey, subVal, depth + 1)
                if subTd:
                    subTable += "<tr>%s</tr>" % subTd
            return "<table border='0'>%s</table>" % subTable

        # Note: Recursive list are not very visible.
        if isinstance(val, (list, tuple)):
            # If this is an empty list or tuple.
            if not val:
                # return "(Empty)"
                # Empty set character in UTF8
                return "{" + "&#x2205;" + "}"
            if depth % 2 == 0:
                subTable = ""
                for subElement in val:
                    subTd = FormatElement(subElement, depth + 1)
                    subTable += "<tr>%s</tr>" % subTd
                return "<table border='0'>%s</table>" % subTable
            else:
                subTable = ""
                for subElement in val:
                    subTd = FormatElement(subElement, depth + 1)
                    subTable += subTd
                return "<table border='0'><tr>%s</tr></table>" % subTable

        try:
            decodVal = json.loads(val)
            return FormatElementAux(decodVal, depth + 1)

        except ValueError:
            # It is a string which cannot be converted to json.
            val = cgi.escape(val)
            return lib_exports.StrWithBr(val)
        except TypeError:
            # "Expected a string or buffer"
            # It is not a string, so it could be a datetime.datetime
            val = cgi.escape(str(val))
            return lib_exports.StrWithBr(val)
        return "FormatElement failure"

    def FormatElement(val, depth=0):
        if lib_kbase.IsLink(val):
            valTitle = "FormatElement " + ExternalToTitle(val)
            valTitleUL = lib_exports.DotUL(valTitle)
            return "<td align='left' balign='left' border='0' href='%s'>%s</td>" % (
                val, valTitleUL)

        resStr = FormatElementAux(val, depth)
        return "<td align='left' balign='left' border='0'>%s</td>" % resStr

    # Prints a key-value pair as two TD tags, to go in an HTML table.
    def FormatPair(key, val, depth=0):
        colFirst = "<td align='left' valign='top' border='0'>%s</td>" % lib_exports.DotBold(
            key)
        colSecond = FormatElement(val, depth + 1)
        return colFirst + colSecond

    # Display in the DOT node the list of its literal properties.
    def FieldsToHtmlVertical(grph, the_fields):
        props = {}
        idx = 0
        # TODO: The sort must put at first, some specific keys.
        # For example, sources_top/nmap_run.py, the port number as an int (Not a string)
        # Also, filenames, case-sensitive or not.
        for (key, val) in sorted(the_fields):
            # This should come first, but it does not so we prefix with "----". Hack !
            if key == pc.property_information:
                # Completely left-aligned. Col span is 2, approximate ratio.
                val = lib_exports.StrWithBr(val, 2)
                currTd = "<td align='left' balign='left' colspan='2'>%s</td>" % val
            elif IsFlatProperty(key):
                urlTxt = lib_naming.ParseEntityUri(val)[0]
                splitTxt = lib_exports.StrWithBr(urlTxt, 2)
                # The text of the link must be underlined.
                currTd = '<td href="%s" align="left" colspan="2">%s</td>' % (
                    val, lib_exports.DotUL(splitTxt))
            else:
                key_qname = qname(key, grph)
                # This assumes: type(val) == 'rdflib.term.Literal'
                # sys.stderr.write("FORMAT ELEMENT: %s\n" %(dir(val)))
                if lib_kbase.IsLiteral(val):
                    currTd = FormatPair(key_qname, val.value)
                else:
                    currTd = FormatPair(key_qname, val)

            props[idx] = currTd
            idx += 1
        return props

    # Ca liste les labels des objects qui apparaissent dans les blocs,
    # et pointent vers le nom du record.
    dictCollapsedObjectLabelsToSubjectLabels = {}

    # This contains, for each node (subject), the related node (object) linked
    # to it with a property to be displayed in tables instead of individual nodes.
    dictPropsCollapsedSubjectsToObjectLists = {}

    for collapsPropObj in CollapsedProperties:
        collapsPropNam = lib_exports.PropToShortPropNam(collapsPropObj)
        dictPropsCollapsedSubjectsToObjectLists[
            collapsPropNam] = collections.defaultdict(list)

    # TODO: (TRANSLATE THIS) Une premiere passe pour batir l'arbre d'une certaine propriete.
    # Si pas un DAG, tant pis, ca fera un lien en plus.
    # ON voulait batir des records, mais les nodes dans un record ne peuvent pas
    # avoir un URL: Donc ca va pas evidemment.
    # HTML-LIKE Labels avec PORT et PORTPOS.
    # CA VA AUSSI SIMPLIFIER L'AFFICHAGE DES TRUCS ENORMES: Modules, Fichiers etc...
    # Et on pourra trier car il y a un ordre.
    # Donc ca doit etre facile d'ajouter des proprietes affichees comme ca.

    logfil.write(TimeStamp() + " Rdf2Dot: First pass\n")

    # New intermediary node created.
    def CollapsedLabel(collapsPropNam, subjNam):
        return "R_" + collapsPropNam + "_" + subjNam

    # Called mainly from entity.py. If S points vers O, transforms "O" => "R_S:O"
    # Accordingly we create an edge: "S" => "R_S"
    def SubjNamFromCollapsed(collapsPropNam, subjNam):
        #sys.stderr.write("ADDING1 subjNam=%s collapsPropNam=%s\n" % (subjNam,collapsPropNam))
        collapsedSubjNam = dictCollapsedObjectLabelsToSubjectLabels[subjNam][
            collapsPropNam]
        #sys.stderr.write("ADDING2 subjNam=%s collapsPropNam=%s\n" % (subjNam,collapsPropNam))
        newSubjNam = CollapsedLabel(collapsPropNam,
                                    collapsedSubjNam) + ":" + subjNam
        #sys.stderr.write("ADDED collapsedSubjNam=%s newSubjNam=%s collapsPropNam=%s\n" % (collapsedSubjNam,newSubjNam,collapsPropNam))
        return newSubjNam

    # This is sorted so the result is deterministic. Very small performance impact.
    # Any order will do as long as the result is always the same.
    sortedGrph = sorted(grph)

    # TODO: Loop only on the "collapsed" properties, the ones whose objects must be displayed
    # in tables, instead of links  - if only they have a single subject. Otherwise it cannot work.
    for subj, prop, obj in sortedGrph:

        # Objects linked with these properties, are listed in a table, instead of distinct nodes in a graph.
        if prop in CollapsedProperties:
            # TODO: We lose the property, unfortunately. Should make a map: subject => prop => object ?
            subjNam = RdfNodeToDotLabel(subj)

            propNam = lib_exports.PropToShortPropNam(prop)
            dictPropsCollapsedSubjectsToObjectLists[propNam][subj].append(obj)

            # Maybe we already entered it: Not a problem.
            objNam = RdfNodeToDotLabel(obj)

            # CollapsedProperties can contain only properties which define a tree,
            # as visibly the "object" nodes can have one ancestor only.
            try:
                # TODO: We should check if a node appears in two tables,
                # associated to two properties and/or two parent node.
                dictCollapsedObjectLabelsToSubjectLabels[objNam][
                    propNam] = subjNam
            except KeyError:
                dictCollapsedObjectLabelsToSubjectLabels[objNam] = dict()
                dictCollapsedObjectLabelsToSubjectLabels[objNam][
                    propNam] = subjNam

    # For getting the node of an object, as it might be in a table.
    def RdfNodeToDotLabelExtended(obj, prop):
        objNam = RdfNodeToDotLabel(obj)

        try:
            dictOfProps = dictCollapsedObjectLabelsToSubjectLabels[objNam]
        except KeyError:
            # sys.stderr.write("RdfNodeToDotLabelExtended propNam=%s objNam=%s\n"%(propNam,objNam) )
            return objNam

        # Let's hope there is only one collapsed property for this node. Otherwise, it means
        # that this node would be displayed in two different tables. It happened...
        if not prop is None:
            propNam = lib_exports.PropToShortPropNam(prop)
            try:
                # Maybe this property is not collapsed.
                subjNam = dictOfProps[propNam]
            except KeyError:
                prop = None

        # Maybe the property is not known, if the node is the subject.
        # Or the property is not collapsed.
        if prop is None:
            # In Python3, keys() is an iterable. No need to create a list.
            #propNam = list(dictOfProps.keys())[0]
            #propNam = dictOfProps.keys()[0]
            for propNam in dictOfProps.keys():
                break
            # First property available.
            subjNam = dictOfProps[propNam]

        newObjNam = CollapsedLabel(propNam, subjNam) + ":" + objNam
        return newObjNam

    # Now we know that we have seen all nodes in a collapsed property.
    for subj, prop, obj in sortedGrph:
        if prop in CollapsedProperties:
            continue

        # Maybe the subject node belongs to a table, but the property is not known.
        subjNam = RdfNodeToDotLabelExtended(subj, None)
        if lib_kbase.IsLink(obj):

            prp_col = lib_properties.prop_color(prop)

            # TODO: GENERALIZE THIS TO ALL COMMUTATIVE PROPERTIES.
            # THAT IS: PROPERTIES WHOSE TRIPLES ARE MERGED WHEN
            # WE HAVE AT THE SAME TIME: (Subj,Prop,Obj) and (Obj,Prop,Subj).
            # WHEN THIS HAPPENS, THE ARROW MUST BE BIDIRECTIONAL.
            # TODO: All commutative relation have bidirectional arrows.
            # At the moment, only one property can be bidirectional.
            if prop == pc.property_socket_end:

                # BEWARE, MAYBE THIS IS A PORT INTO A TABLE. SO IT HAS TO BE PREFIXED BY THE RECORD NAME.
                objNam = RdfNodeToDotLabelExtended(obj, prop)
                if (obj, prop, subj) in grph:
                    if subjNam < objNam:
                        stream.write(
                            pattEdgeBiDir %
                            (subjNam, objNam, prp_col, qname(prop, grph)))
                else:
                    # One connection only: We cannot see the other.
                    stream.write(pattEdgeOrien %
                                 (subjNam, objNam, prp_col, qname(prop, grph)))
            elif prop in [
                    pc.property_rdf_data_nolist1, pc.property_rdf_data_nolist2,
                    pc.property_rdf_data_nolist3
            ]:
                # TODO: Il suffit de tester si obj est un url de la forme "entity.py" ???
                # HTML and images urls can be "flattened" because the nodes have no descendants.
                # Do not create a node for this.
                # MIME links displayed in the same column as sub-directory.
                # Also, it might be enough to test if the object has the form "entity.py" because it has no descendant.
                # TODO: CGIPROP: Can it have several html or sub-rdf ?? It is necessary !
                fieldsSet[subj].append((prop, obj))
            else:
                objNam = RdfNodeToDotLabelExtended(obj, prop)
                # C est la que si subjNam est dans une liste de dictCollapsedSubjectsToObjectLists,
                # il faut rajouter devant, le nom du record, c est a dire SON subjNam + "_table_rdf_data:".
                try:
                    # Syntax with colon required by DOT.
                    propNam = lib_exports.PropToShortPropNam(prop)
                    subjNam = SubjNamFromCollapsed(propNam, subjNam)
                except KeyError:
                    # sys.stderr.write("PASS subjNam=%s objNam=%s\n"%(subjNam,objNam))
                    pass

                stream.write(pattEdgeOrien %
                             (subjNam, objNam, prp_col, qname(prop, grph)))
        elif obj == None:
            # No element created in nodes[]
            fieldsSet[subj].append((prop, "Null"))
        else:
            # For Literals. No element created in nodes[]
            # Literals can be processed according to their type.
            # Some specific properties cannot have children so they can be stored as literals?
            # Les proprietes comme "pid", on devrait plutot afficher le lien vers le process, dans la table ???
            # Les URLs de certaines proprietes sont affichees en colonnes.
            # Ou bien pour ces proprietes, on recree un entity.py ??

            fieldsSet[subj].append((prop, obj))

    logfil.write(TimeStamp() +
                 " Rdf2Dot: Replacing vectors: CollapsedProperties=%d.\n" %
                 (len(CollapsedProperties)))

    # Now, replaces each vector by a single object containg a HTML table.
    # TODO: Unfortunately, the prop is lost, which implies that all children are mixed together.

    def ProcessCollapsedProperties(propNam):
        dictCollapsedSubjectsToObjectLists = dictPropsCollapsedSubjectsToObjectLists[
            propNam]
        logfil.write(TimeStamp() +
                     " Rdf2Dot: dictCollapsedSubjectsToObjectLists=%d.\n" %
                     (len(dictCollapsedSubjectsToObjectLists)))

        for subjUrl, nodLst in lib_util.six_iteritems(
                dictCollapsedSubjectsToObjectLists):
            subjNam = RdfNodeToDotLabel(subjUrl)

            subjNamTab = CollapsedLabel(propNam, subjNam)
            try:
                # TODO: Cette logique ajoute parfois un niveau de noeud inutile. Plus exactement, ca duplique un noeud.
                # Ou plus exactement, le noed est represente par deux objects graphiques:
                # * Un qui a les scripts.
                # * Un autre qui a la liste HTML qu on fabrique.
                # => Peut-on imaginer de melanger les deux ??
                # Dans WritePatterns: Ajouter le nom du noeud au label.
                subjNam = SubjNamFromCollapsed(propNam, subjNam)
            except KeyError:
                pass

            # Point from the subject to the table containing the objects.
            stream.write(pattEdgeOrien %
                         (subjNam, subjNamTab, "GREEN", propNam))

            (labText, subjEntityGraphicClass,
             entity_id) = lib_naming.ParseEntityUri(subjUrl)

            # Probleme avec les champs:
            # Faire une premiere passe et reperer les fields, detecter les noms des colonnes, leur attribuer ordre et indice.
            # Seconde passe pour batir les lignes.
            # Donc on ordonne toutes les colonnes.
            # Pour chaque field: les prendre dans le sens du header et quand il y a un trou, colonne vide.
            # Inutile de trier les field, mais il d'abord avoir une liste complete des champs, dans le bon sens.
            # CA SUPPOSE QUE DANS FIELDSSET LES KEYS SONT UNIQUES.
            # SI ON NE PEUT PAS, ALORS ON METTRA DES LISTES. MAIS CETTE CONTRAINTE SIMPLIFIE L'AFFICHAGE.

            # DOMMAGE QU ON SCANNE LES OBJETS DEUX FOIS UNIQUEMENT POUR AVOIR LES NOMS DES CHAMPS !!!!!!!!!!!!!
            # TODO: HEURISTIQUE: ON pourrait s'arreter aux dix premiers. Ou bien faire le tri avant ?
            # On bien prendre les colonnes de la premiere ligne, et recommencer si ca ne marche pas.
            # Unique columns of the descendant of this subject.
            rawFieldsKeys = set()
            for obj in nodLst:
                # One table per node.
                rawFieldsKeys.update(fld[0] for fld in fieldsSet[obj])

            # sys.stderr.write("rawFieldsKeys BEFORE =%s\n" % str(rawFieldsKeys) )

            # Mandatory properties must come at the beginning of the columns of the header, with first indices.
            # BUG: Si on retire html de cette liste alors qu il y a des valeurs, colonnes absentes.
            # S il y a du html ou du RDF, on veut que ca vienne en premier.
            fieldsKeysOrdered = []
            for fldPriority in FlatPropertertiesList:
                try:
                    # Must always be appended. BUT IF THERE IS NO html_data, IS IT WORTH ?
                    # TODO: Remove if not HTML and no sub-rdf. CGIPROP

                    # If the property is never used, exception then next property.
                    rawFieldsKeys.remove(fldPriority)
                    fieldsKeysOrdered.append(fldPriority)
                except KeyError:
                    pass

            # This one is always removed because its content is concatenated at the first column.
            for fldToRemove in [pc.property_information]:
                try:
                    rawFieldsKeys.remove(fldToRemove)
                except KeyError:
                    pass

            # Appends rest of properties, sorted.
            fieldsKeys = fieldsKeysOrdered + sorted(rawFieldsKeys)

            # sys.stderr.write("fieldsKeys=%s\n" % str(fieldsKeys) )

            # This assumes that the header columns are sorted.
            keyIndices = {
                nameKey: indexKey
                for (indexKey, nameKey) in enumerate(fieldsKeys, 1)
            }

            numberKeys = len(keyIndices) + 1

            # Apparently, no embedded tables.
            dictHtmlLines = dict()
            for objUri in nodLst:
                # One table per node.
                subObjId = RdfNodeToDotLabel(objUri)

                # Beware "\L" which should not be replaced by "<TABLE>" but this is not the right place.
                subNodUri = objUri.replace('&', '&amp;')

                try:
                    (subObjNam, subEntityGraphicClass,
                     subEntityId) = lib_naming.ParseEntityUriShort(objUri)
                except UnicodeEncodeError:
                    sys.stderr.write("UnicodeEncodeError error:%s\n" %
                                     (objUri))
                    (subObjNam, subEntityGraphicClass,
                     subEntityId) = ("Utf problem1", "Utf problem2",
                                     "Utf problem3")

                # sys.stderr.write("subEntityGraphicClass=%s\n"%subEntityGraphicClass)

                # If this is a script, always displayed on white, even if reletd to a specific entity.
                # THIS IS REALLY A SHAME BECAUSE WE JUST NEED THE ORIGINAL PROPERTY.
                if objUri.find("entity.py") < 0:
                    objColor = "#FFFFFF"
                else:
                    objColor = lib_patterns.EntityClassToColor(
                        subEntityGraphicClass)
                # This lighter cololor for the first column.
                objColorLight = lib_patterns.ColorLighter(objColor)

                # Some colors a bit clearer ? Or take the original color of the class ?
                td_bgcolor_plain = '<td BGCOLOR="%s" ' % objColor
                td_bgcolor_light = '<td BGCOLOR="%s" ' % objColorLight
                td_bgcolor = td_bgcolor_plain

                # Some columns might not have a value. The first column is for the key.
                columns = [td_bgcolor + " ></td>"] * numberKeys

                # Just used for the vertical order of lines, one line per object.
                title = ""

                # TODO: CGIPROP. This is not a dict, the same key can appear several times ?
                for (key, val) in fieldsSet[objUri]:
                    if key == pc.property_information:
                        # This can be a short string only.
                        title += val
                        continue

                    # TODO: This is hard-coded.
                    if IsFlatProperty(key):

                        # In fact, it might also be an internal URL with "entity.py"
                        if lib_kbase.IsLiteral(val):
                            if isinstance(val.value, (list, tuple)):
                                strHtml = FormatElementAux(val.value)
                                sys.stderr.write("val.value=%s\n" % strHtml)
                                tmpCell = td_bgcolor + 'align="left">%s</td>' % strHtml
                            else:
                                tmpCell = td_bgcolor + 'align="left">%s</td>' % val.value
                        else:
                            valTitle = lib_naming.ParseEntityUri(val)[0]

                            valTitleUL = lib_exports.DotUL(valTitle)
                            tmpCell = td_bgcolor + 'href="%s" align="left" >%s</td>' % (
                                val, valTitleUL)

                    else:
                        try:
                            float(val)
                            tmpCell = td_bgcolor + 'align="right">%s</td>' % val
                        except:
                            # Wraps the string if too long. Can happen only with a literal.
                            tmpCell = td_bgcolor + 'align="left">%s</td>' % lib_exports.StrWithBr(
                                val)

                    idxKey = keyIndices[key]
                    columns[idxKey] = tmpCell

                if title:
                    title_key = title
                else:
                    title_key = subObjNam

                # Maybe the first column is a literal ?
                if subEntityId != "PLAINTEXTONLY":
                    # WE SHOULD PROBABLY ESCAPE HERE TOO.
                    columns[
                        0] = td_bgcolor_light + 'port="%s" href="%s" align="LEFT" >%s</td>' % (
                            subObjId, subNodUri, title_key)
                else:
                    subNodUri = cgi.escape(subNodUri)
                    columns[
                        0] = td_bgcolor_light + 'port="%s" align="LEFT" >%s</td>' % (
                            subObjId, subNodUri)

                # Several scripts might have the same help text, so add a number.
                # "Title" => "Title"
                # "Title" => "Title/2"
                # "Title" => "Title/3" etc...
                # Beware that it is quadratic with the number of scripts with identical info.
                title_idx = 2
                title_uniq = title_key
                while title_uniq in dictHtmlLines:
                    title_uniq = "%s/%d" % (title_key, title_idx)
                    title_idx += 1

                # TODO: L'ordre est base sur les chaines mais devrait etre base sur le contenu. Exemple:
                # TODO: "(TUT_UnixProcess) Handle=10" vient avant "(TUT_UnixProcess) Handle=2"
                # TODO: title_uniq devrait etre plutot la liste des proprietes.
                # TODO: By clicking on the column names, we could change the order.
                dictHtmlLines[title_uniq] = "".join(columns)

            # Replace the first column by more useful information.
            numNodLst = len(nodLst)

            # TODO: Compute this once for all.
            eltNam = subEntityGraphicClass.split("/")[-1]
            if not eltNam:
                # TODO: This is not the right criteria. Must select if we are listing scripts.
                eltNam = "script"

            eltNamPlural = lib_grammar.ToPlural(eltNam, numNodLst)
            txtElements = "%d %s" % (numNodLst, eltNamPlural)
            header = '<td border="1">' + lib_exports.DotBold(
                txtElements) + "</td>"

            # TODO: Replace each column name with a link which sorts the line based on this column.
            # The order of columns could be specified with an extra cgi argument with the columns names.
            for key in fieldsKeys:
                columnTitle = qname(key, grph)
                columnTitle = columnTitle.replace("_", " ").capitalize()
                header += "<td border='1'>" + lib_exports.DotBold(
                    columnTitle) + "</td>"
            # With an empty key, it comes first when sorting.
            dictHtmlLines[""] = header

            # MAYBE SHOULD BE DONE TWICE !!!!! SEE ALSO ELSEWHERE !!!!
            subjUrlClean = subjUrl.replace('&', '&amp;')

            # ATTENTION: La forme du record est celle du sujet.
            # ca veut donc dire qu'on va avoir la meme couleur pour des objets de types
            # differents s'ils sont dans la meme relation avec un sujet identique ?
            numFields = len(fieldsKeys) + 1

            # The label might be truncated
            if subjEntityGraphicClass:
                helpText = "List of " + subjEntityGraphicClass + " objects in " + labText
            else:
                helpText = "List of scripts in " + labText

            # TODO: Le title and the content are not necessarily of the same class.
            # labTextWithBr is the first line of the table containing nodes linked with the
            # same property. Unfortunately we have lost this property.
            labText = lib_exports.TruncateInSpace(labText, 30)
            labTextWithBr = lib_exports.StrWithBr(labText)
            labTextWithBr += ": " + propNam

            if entity_id == "PLAINTEXTONLY":
                subjUrlClean = ""

            # This color is the table's contour.
            lib_patterns.WritePatterned(stream, subjEntityGraphicClass,
                                        subjNamTab, helpText, '"#000000"',
                                        subjUrlClean, numFields, labTextWithBr,
                                        dictHtmlLines)

            # TODO: Eviter les repetitions de la meme valeur dans une colonne en comparant d une ligne a l autre.
            # TODO: Si une cellule est identique jusqu a un delimiteur, idem, remplacer par '"'.

    if CollapsedProperties:
        for collapsedProp in CollapsedProperties:
            collapsedPropNam = lib_exports.PropToShortPropNam(collapsedProp)
            ProcessCollapsedProperties(collapsedPropNam)

    logfil.write(TimeStamp() +
                 " Rdf2Dot: Display remaining nodes. dictRdf2Dot=%d\n" %
                 len(dictRdf2Dot))

    # Now, display the normal nodes, which are not displayed in tables.
    for objRdfNode, objLabel in lib_util.six_iteritems(dictRdf2Dot):
        # TODO: Avoids this lookup.
        if objLabel in dictCollapsedObjectLabelsToSubjectLabels:
            continue

        objPropsAsHtml = FieldsToHtmlVertical(grph, fieldsSet[objRdfNode])

        labHRef = objRdfNode.replace('&', '&amp;')

        try:
            # TODO: Probleme ici: La chaine est deja codee pour HTML ce qui en rend le parsing different
            # TODO: ... de celui d'un URL deja decode. DOMMAGE: On quote puis unquote !!!
            (labText, objEntityGraphClass,
             entity_id) = lib_naming.ParseEntityUri(
                 lib_util.urllib_unquote(objRdfNode))
        except UnicodeEncodeError:
            sys.stderr.write("UnicodeEncodeError error:%s\n" % (objRdfNode))

        # WritePatterned receives an list of strings similar to "<td>jhh</td><td>jhh</td><td>jhh</td>"
        # This function adds <tr> and </tr> on both sides.
        # This avoids concatenations.

        # Ampersand are intentionnally doubled, because later on they are replaced twice.
        # That is, interpreted twice as HTML entities.
        # This might be temporary until we replace CGI arguments by genuine WMI Monikers.
        labTextNoAmp = labText.replace("&amp;amp;", " ")
        labTextNoAmp = labTextNoAmp.strip()
        labTextClean = lib_exports.StrWithBr(labTextNoAmp)
        # Two columns because it encompasses the key and the value.

        if objEntityGraphClass:
            helpText = labTextNoAmp + " is a " + objEntityGraphClass
        else:
            if labTextClean.startswith("http"):
                helpText = "External URL " + labTextNoAmp
            else:
                helpText = "Script " + labTextNoAmp

        # This color is the object's contour.
        lib_patterns.WritePatterned(stream, objEntityGraphClass, objLabel,
                                    helpText, '"#000000"', labHRef, 2,
                                    labTextClean, objPropsAsHtml)

    logfil.write(TimeStamp() + " Rdf2Dot: Leaving\n")
    stream.write("}\n")
Example #7
0
    def ProcessCollapsedProperties(propNam):
        dictCollapsedSubjectsToObjectLists = dictPropsCollapsedSubjectsToObjectLists[
            propNam]
        logfil.write(TimeStamp() +
                     " Rdf2Dot: dictCollapsedSubjectsToObjectLists=%d.\n" %
                     (len(dictCollapsedSubjectsToObjectLists)))

        for subjUrl, nodLst in lib_util.six_iteritems(
                dictCollapsedSubjectsToObjectLists):
            subjNam = RdfNodeToDotLabel(subjUrl)

            subjNamTab = CollapsedLabel(propNam, subjNam)
            try:
                # TODO: Cette logique ajoute parfois un niveau de noeud inutile. Plus exactement, ca duplique un noeud.
                # Ou plus exactement, le noed est represente par deux objects graphiques:
                # * Un qui a les scripts.
                # * Un autre qui a la liste HTML qu on fabrique.
                # => Peut-on imaginer de melanger les deux ??
                # Dans WritePatterns: Ajouter le nom du noeud au label.
                subjNam = SubjNamFromCollapsed(propNam, subjNam)
            except KeyError:
                pass

            # Point from the subject to the table containing the objects.
            stream.write(pattEdgeOrien %
                         (subjNam, subjNamTab, "GREEN", propNam))

            (labText, subjEntityGraphicClass,
             entity_id) = lib_naming.ParseEntityUri(subjUrl)

            # Probleme avec les champs:
            # Faire une premiere passe et reperer les fields, detecter les noms des colonnes, leur attribuer ordre et indice.
            # Seconde passe pour batir les lignes.
            # Donc on ordonne toutes les colonnes.
            # Pour chaque field: les prendre dans le sens du header et quand il y a un trou, colonne vide.
            # Inutile de trier les field, mais il d'abord avoir une liste complete des champs, dans le bon sens.
            # CA SUPPOSE QUE DANS FIELDSSET LES KEYS SONT UNIQUES.
            # SI ON NE PEUT PAS, ALORS ON METTRA DES LISTES. MAIS CETTE CONTRAINTE SIMPLIFIE L'AFFICHAGE.

            # DOMMAGE QU ON SCANNE LES OBJETS DEUX FOIS UNIQUEMENT POUR AVOIR LES NOMS DES CHAMPS !!!!!!!!!!!!!
            # TODO: HEURISTIQUE: ON pourrait s'arreter aux dix premiers. Ou bien faire le tri avant ?
            # On bien prendre les colonnes de la premiere ligne, et recommencer si ca ne marche pas.
            # Unique columns of the descendant of this subject.
            rawFieldsKeys = set()
            for obj in nodLst:
                # One table per node.
                rawFieldsKeys.update(fld[0] for fld in fieldsSet[obj])

            # sys.stderr.write("rawFieldsKeys BEFORE =%s\n" % str(rawFieldsKeys) )

            # Mandatory properties must come at the beginning of the columns of the header, with first indices.
            # BUG: Si on retire html de cette liste alors qu il y a des valeurs, colonnes absentes.
            # S il y a du html ou du RDF, on veut que ca vienne en premier.
            fieldsKeysOrdered = []
            for fldPriority in FlatPropertertiesList:
                try:
                    # Must always be appended. BUT IF THERE IS NO html_data, IS IT WORTH ?
                    # TODO: Remove if not HTML and no sub-rdf. CGIPROP

                    # If the property is never used, exception then next property.
                    rawFieldsKeys.remove(fldPriority)
                    fieldsKeysOrdered.append(fldPriority)
                except KeyError:
                    pass

            # This one is always removed because its content is concatenated at the first column.
            for fldToRemove in [pc.property_information]:
                try:
                    rawFieldsKeys.remove(fldToRemove)
                except KeyError:
                    pass

            # Appends rest of properties, sorted.
            fieldsKeys = fieldsKeysOrdered + sorted(rawFieldsKeys)

            # sys.stderr.write("fieldsKeys=%s\n" % str(fieldsKeys) )

            # This assumes that the header columns are sorted.
            keyIndices = {
                nameKey: indexKey
                for (indexKey, nameKey) in enumerate(fieldsKeys, 1)
            }

            numberKeys = len(keyIndices) + 1

            # Apparently, no embedded tables.
            dictHtmlLines = dict()
            for objUri in nodLst:
                # One table per node.
                subObjId = RdfNodeToDotLabel(objUri)

                # Beware "\L" which should not be replaced by "<TABLE>" but this is not the right place.
                subNodUri = objUri.replace('&', '&amp;')

                try:
                    (subObjNam, subEntityGraphicClass,
                     subEntityId) = lib_naming.ParseEntityUriShort(objUri)
                except UnicodeEncodeError:
                    sys.stderr.write("UnicodeEncodeError error:%s\n" %
                                     (objUri))
                    (subObjNam, subEntityGraphicClass,
                     subEntityId) = ("Utf problem1", "Utf problem2",
                                     "Utf problem3")

                # sys.stderr.write("subEntityGraphicClass=%s\n"%subEntityGraphicClass)

                # If this is a script, always displayed on white, even if reletd to a specific entity.
                # THIS IS REALLY A SHAME BECAUSE WE JUST NEED THE ORIGINAL PROPERTY.
                if objUri.find("entity.py") < 0:
                    objColor = "#FFFFFF"
                else:
                    objColor = lib_patterns.EntityClassToColor(
                        subEntityGraphicClass)
                # This lighter cololor for the first column.
                objColorLight = lib_patterns.ColorLighter(objColor)

                # Some colors a bit clearer ? Or take the original color of the class ?
                td_bgcolor_plain = '<td BGCOLOR="%s" ' % objColor
                td_bgcolor_light = '<td BGCOLOR="%s" ' % objColorLight
                td_bgcolor = td_bgcolor_plain

                # Some columns might not have a value. The first column is for the key.
                columns = [td_bgcolor + " ></td>"] * numberKeys

                # Just used for the vertical order of lines, one line per object.
                title = ""

                # TODO: CGIPROP. This is not a dict, the same key can appear several times ?
                for (key, val) in fieldsSet[objUri]:
                    if key == pc.property_information:
                        # This can be a short string only.
                        title += val
                        continue

                    # TODO: This is hard-coded.
                    if IsFlatProperty(key):

                        # In fact, it might also be an internal URL with "entity.py"
                        if lib_kbase.IsLiteral(val):
                            if isinstance(val.value, (list, tuple)):
                                strHtml = FormatElementAux(val.value)
                                sys.stderr.write("val.value=%s\n" % strHtml)
                                tmpCell = td_bgcolor + 'align="left">%s</td>' % strHtml
                            else:
                                tmpCell = td_bgcolor + 'align="left">%s</td>' % val.value
                        else:
                            valTitle = lib_naming.ParseEntityUri(val)[0]

                            valTitleUL = lib_exports.DotUL(valTitle)
                            tmpCell = td_bgcolor + 'href="%s" align="left" >%s</td>' % (
                                val, valTitleUL)

                    else:
                        try:
                            float(val)
                            tmpCell = td_bgcolor + 'align="right">%s</td>' % val
                        except:
                            # Wraps the string if too long. Can happen only with a literal.
                            tmpCell = td_bgcolor + 'align="left">%s</td>' % lib_exports.StrWithBr(
                                val)

                    idxKey = keyIndices[key]
                    columns[idxKey] = tmpCell

                if title:
                    title_key = title
                else:
                    title_key = subObjNam

                # Maybe the first column is a literal ?
                if subEntityId != "PLAINTEXTONLY":
                    # WE SHOULD PROBABLY ESCAPE HERE TOO.
                    columns[
                        0] = td_bgcolor_light + 'port="%s" href="%s" align="LEFT" >%s</td>' % (
                            subObjId, subNodUri, title_key)
                else:
                    subNodUri = cgi.escape(subNodUri)
                    columns[
                        0] = td_bgcolor_light + 'port="%s" align="LEFT" >%s</td>' % (
                            subObjId, subNodUri)

                # Several scripts might have the same help text, so add a number.
                # "Title" => "Title"
                # "Title" => "Title/2"
                # "Title" => "Title/3" etc...
                # Beware that it is quadratic with the number of scripts with identical info.
                title_idx = 2
                title_uniq = title_key
                while title_uniq in dictHtmlLines:
                    title_uniq = "%s/%d" % (title_key, title_idx)
                    title_idx += 1

                # TODO: L'ordre est base sur les chaines mais devrait etre base sur le contenu. Exemple:
                # TODO: "(TUT_UnixProcess) Handle=10" vient avant "(TUT_UnixProcess) Handle=2"
                # TODO: title_uniq devrait etre plutot la liste des proprietes.
                # TODO: By clicking on the column names, we could change the order.
                dictHtmlLines[title_uniq] = "".join(columns)

            # Replace the first column by more useful information.
            numNodLst = len(nodLst)

            # TODO: Compute this once for all.
            eltNam = subEntityGraphicClass.split("/")[-1]
            if not eltNam:
                # TODO: This is not the right criteria. Must select if we are listing scripts.
                eltNam = "script"

            eltNamPlural = lib_grammar.ToPlural(eltNam, numNodLst)
            txtElements = "%d %s" % (numNodLst, eltNamPlural)
            header = '<td border="1">' + lib_exports.DotBold(
                txtElements) + "</td>"

            # TODO: Replace each column name with a link which sorts the line based on this column.
            # The order of columns could be specified with an extra cgi argument with the columns names.
            for key in fieldsKeys:
                columnTitle = qname(key, grph)
                columnTitle = columnTitle.replace("_", " ").capitalize()
                header += "<td border='1'>" + lib_exports.DotBold(
                    columnTitle) + "</td>"
            # With an empty key, it comes first when sorting.
            dictHtmlLines[""] = header

            # MAYBE SHOULD BE DONE TWICE !!!!! SEE ALSO ELSEWHERE !!!!
            subjUrlClean = subjUrl.replace('&', '&amp;')

            # ATTENTION: La forme du record est celle du sujet.
            # ca veut donc dire qu'on va avoir la meme couleur pour des objets de types
            # differents s'ils sont dans la meme relation avec un sujet identique ?
            numFields = len(fieldsKeys) + 1

            # The label might be truncated
            if subjEntityGraphicClass:
                helpText = "List of " + subjEntityGraphicClass + " objects in " + labText
            else:
                helpText = "List of scripts in " + labText

            # TODO: Le title and the content are not necessarily of the same class.
            # labTextWithBr is the first line of the table containing nodes linked with the
            # same property. Unfortunately we have lost this property.
            labText = lib_exports.TruncateInSpace(labText, 30)
            labTextWithBr = lib_exports.StrWithBr(labText)
            labTextWithBr += ": " + propNam

            if entity_id == "PLAINTEXTONLY":
                subjUrlClean = ""

            # This color is the table's contour.
            lib_patterns.WritePatterned(stream, subjEntityGraphicClass,
                                        subjNamTab, helpText, '"#000000"',
                                        subjUrlClean, numFields, labTextWithBr,
                                        dictHtmlLines)
Example #8
0
def AddImportedModules(grph, node, filNam, maxDepth, dispPackages, dispFiles):
    sys.stderr.write(
        "AddImportedModules filNam=%s dispPackages=%d dispFiles=%d\n" %
        (filNam, dispPackages, dispFiles))
    filename, file_extension = os.path.splitext(filNam)
    filextlo = file_extension.lower()
    if filextlo not in [".py", ".pyw"]:
        return

    finder = modulefinder.ModuleFinder()
    try:
        finder.run_script(filNam)
    except TypeError:
        exc = sys.exc_info()[0]
        lib_common.ErrorMessageHtml("Error loading Python script %s:%s" %
                                    (filNam, str(exc)))

    AddImportedModules.dictModules = dict()

    # A cache which associates a node to a Python module name.
    def GetModuNode(moduNam):
        try:
            moduNode = AddImportedModules.dictModules[moduNam]
        except KeyError:
            moduNode = MakeUri(moduNam)
            AddImportedModules.dictModules[moduNam] = moduNode
        return moduNode

    AddImportedModules.dictFiles = dict()

    # A cache which associates a node to a file name.
    def GetFileNode(moduFil):
        try:
            fileNode = AddImportedModules.dictModules[moduFil]
        except KeyError:
            fileNode = lib_uris.gUriGen.FileUri(moduFil)
            AddImportedModules.dictModules[moduFil] = fileNode
        return fileNode

    for moduNam, mod in lib_util.six_iteritems(finder.modules):
        splitNam = moduNam.split(".")
        # sys.stderr.write("splitNam=%s\n"%str(splitNam))
        # sys.stderr.write("mod=%s\n"%str(mod))
        moduFil = mod.__file__
        # sys.stderr.write("moduFil=%s\n"%moduFil)

        if len(splitNam) > maxDepth:
            continue

        if dispPackages:
            moduNod = GetModuNode(moduNam)

            if dispFiles and moduFil:
                nodeFile = GetFileNode(moduFil)
                # nodeFile is the result of lib_common.NodeUrl
                grph.add((moduNod, pc.property_rdf_data_nolist2, nodeFile))

            if len(splitNam) == 1:
                grph.add((node, propPythonPackage, moduNod))
                sys.stderr.write("No parent: moduNam=%s\n" % (moduNam))
            else:
                parentModuNam = ".".join(splitNam[:-1])
                parentModuNod = GetModuNode(parentModuNam)
                grph.add((parentModuNod, propPythonRequires, moduNod))
                sys.stderr.write("parentModuNam=%s moduNam=%s\n" %
                                 (parentModuNam, moduNam))

        if dispFiles and not dispPackages:
            if moduFil:
                nodeFile = GetFileNode(moduFil)
                if len(splitNam) == 1:
                    # TODO: Should be connected to the module.
                    grph.add((node, propPythonPackage, nodeFile))
                    # TODO: LE RAJOUTER QUAND MEME SINON ON NE VOIT RIEN !
                    pass
                else:
                    parentModuNam = ".".join(splitNam[:-1])
                    parentModuNod = GetModuNode(parentModuNam)
                    grph.add((parentModuNod, propPythonRequires, nodeFile))
Example #9
0
    def ProcessCollapsedProperties(propNam):
        dictCollapsedSubjectsToObjectLists = dict_props_collapsed_subjects_to_object_lists[
            propNam]
        logfil.write(TimeStamp() +
                     " Rdf2Dot: dictCollapsedSubjectsToObjectLists=%d.\n" %
                     (len(dictCollapsedSubjectsToObjectLists)))

        for subjUrl, nodLst in lib_util.six_iteritems(
                dictCollapsedSubjectsToObjectLists):
            subjNam = _rdf_node_to_dot_label(subjUrl)

            subjNamTab = CollapsedLabel(propNam, subjNam)
            try:
                # TODO: This logic adds an extra level of node: Try to flatten the tree.
                subjNam = SubjNamFromCollapsed(propNam, subjNam)
            except KeyError:
                pass

            # This points from the subject to the table containing the objects.
            # TODO: This color should be a parameter.
            stream.write(_pattern_edge_oriented %
                         (subjNam, subjNamTab, "GREEN", propNam))

            (labText, subjEntityGraphicClass,
             entity_id) = lib_naming.ParseEntityUri(subjUrl)

            # At the moment, two passes are necessary:
            # * A first pass to create the compte list of fields, because they might be a bit different
            #   from one record to the other. The column names pf these fields get an unique index number
            #   and can therefore be sorted.
            # * A second pass uses these result, to display the lines.
            #
            # This could be faster by assuming that the first ten columns have all the fields.
            # We could then start the second pass, and if an undetected column is found,
            # then restart from scratch.

            # Unique columns of the descendant of this subject.
            rawFieldsKeys = set()
            for obj in nodLst:
                # One table per node.
                rawFieldsKeys.update(fld[0] for fld in fieldsSet[obj])

            # sys.stderr.write("rawFieldsKeys BEFORE =%s\n" % str(rawFieldsKeys) )

            # Mandatory properties must come at the beginning of the columns of the header, with first indices.
            # BUG: Si on retire html de cette liste alors qu il y a des valeurs, colonnes absentes.
            # S il y a du html ou du RDF, on veut que ca vienne en premier.
            fieldsKeysOrdered = []
            for fldPriority in _flat_properties_list:
                try:
                    # Must always be appended. BUT IF THERE IS NO html_data, IS IT WORTH ?
                    # TODO: Remove if not HTML and no sub-rdf. CGIPROP

                    # If the property is never used, exception then next property.
                    rawFieldsKeys.remove(fldPriority)
                    fieldsKeysOrdered.append(fldPriority)
                except KeyError:
                    pass

            # This one is always removed because its content is concatenated at the first column.
            for fldToRemove in [pc.property_information]:
                try:
                    rawFieldsKeys.remove(fldToRemove)
                except KeyError:
                    pass

            # Appends rest of properties, sorted.
            fieldsKeys = fieldsKeysOrdered + sorted(rawFieldsKeys)

            # sys.stderr.write("fieldsKeys=%s\n" % str(fieldsKeys) )

            # This assumes that the header columns are sorted.
            keyIndices = {
                nameKey: indexKey
                for (indexKey, nameKey) in enumerate(fieldsKeys, 1)
            }

            numberKeys = len(keyIndices) + 1

            # Apparently, no embedded tables.
            dictHtmlLines = dict()
            for objUri in nodLst:
                # One table per node.
                subObjId = _rdf_node_to_dot_label(objUri)

                # Beware "\L" which should not be replaced by "<TABLE>" but this is not the right place.
                subNodUri = objUri.replace('&', '&amp;')

                try:
                    (subObjNam, subEntityGraphicClass,
                     subEntityId) = lib_naming.ParseEntityUriShort(objUri)
                except UnicodeEncodeError:
                    WARNING("UnicodeEncodeError error:%s", objUri)
                    (subObjNam, subEntityGraphicClass,
                     subEntityId) = ("Utf problem1", "Utf problem2",
                                     "Utf problem3")

                # sys.stderr.write("subEntityGraphicClass=%s\n"%subEntityGraphicClass)

                # If this is a script, always displayed on white, even if related to a specific entity.
                # THIS IS REALLY A SHAME BECAUSE WE JUST NEED THE ORIGINAL PROPERTY.
                if objUri.find("entity.py") < 0:
                    objColor = "#FFFFFF"
                else:
                    objColor = lib_patterns.EntityClassToColor(
                        subEntityGraphicClass)
                # This lighter cololor for the first column.
                objColorLight = lib_patterns.ColorLighter(objColor)

                # Some colors a bit clearer ? Or take the original color of the class ?
                td_bgcolor_plain = '<td BGCOLOR="%s" ' % objColor
                td_bgcolor_light = '<td BGCOLOR="%s" ' % objColorLight
                td_bgcolor = td_bgcolor_plain

                # Some columns might not have a value. The first column is for the key.
                columns = [td_bgcolor + " ></td>"] * numberKeys

                # Just used for the vertical order of lines, one line per object.
                title = ""

                # TODO: CGIPROP. This is not a dict, the same key can appear several times ?
                for (key, val) in fieldsSet[objUri]:
                    if key == pc.property_information:
                        # This can be a short string only.
                        title += val
                        continue

                    # TODO: This is hard-coded.
                    if is_flat_property(key):

                        # In fact, it might also be an internal URL with "entity.py"
                        if lib_kbase.IsLiteral(val):
                            if isinstance(val.value, (list, tuple)):
                                strHtml = _format_element_aux(val.value)
                                DEBUG("val.value=%s", strHtml)
                                tmpCell = td_bgcolor + 'align="left">%s</td>' % strHtml
                            else:
                                tmpCell = td_bgcolor + 'align="left">%s</td>' % val.value
                        else:
                            # This displays objects in a table: The top-level object must be
                            # in the same host, so there is no need to display a long label.
                            valTitle = lib_naming.ParseEntityUriShort(val)[0]
                            assert isinstance(valTitle, lib_util.six_text_type)

                            # There might be non-ascii characters such as accents etc...
                            try:
                                valTitle.encode('ascii')
                            except UnicodeEncodeError:
                                valTitle = "Not ascii"

                            valTitleUL = lib_exports.DotUL(valTitle)
                            tmpCell = td_bgcolor + 'href="%s" align="left" >%s</td>' % (
                                val, valTitleUL)

                    else:
                        try:
                            float(val)
                            tmpCell = td_bgcolor + 'align="right">%s</td>' % val
                        except:
                            # Wraps the string if too long. Can happen only with a literal.
                            tmpCell = td_bgcolor + 'align="left">%s</td>' % lib_exports.StrWithBr(
                                val)

                    idxKey = keyIndices[key]
                    columns[idxKey] = tmpCell

                if title:
                    title_key = title
                else:
                    title_key = subObjNam

                # Maybe the first column is a literal ?
                if subEntityId != "PLAINTEXTONLY":
                    # WE SHOULD PROBABLY ESCAPE HERE TOO.
                    columns[
                        0] = td_bgcolor_light + 'port="%s" href="%s" align="LEFT" >%s</td>' % (
                            subObjId, subNodUri, title_key)
                else:
                    subNodUri = lib_util.html_escape(subNodUri)
                    columns[
                        0] = td_bgcolor_light + 'port="%s" align="LEFT" >%s</td>' % (
                            subObjId, subNodUri)

                # Several scripts might have the same help text, so add a number.
                # "Title" => "Title"
                # "Title" => "Title/2"
                # "Title" => "Title/3" etc...
                # Beware that it is quadratic with the number of scripts with identical info.
                title_idx = 2
                title_uniq = title_key
                while title_uniq in dictHtmlLines:
                    title_uniq = "%s/%d" % (title_key, title_idx)
                    title_idx += 1

                # TODO: L'ordre est base sur les chaines mais devrait etre base sur le contenu. Exemple:
                # TODO: "(TUT_UnixProcess) Handle=10" vient avant "(TUT_UnixProcess) Handle=2"
                # TODO: title_uniq devrait etre plutot la liste des proprietes.
                # TODO: By clicking on the column names, we could change the order.
                dictHtmlLines[title_uniq] = "".join(columns)

            # Replace the first column by more useful information.
            numNodLst = len(nodLst)

            # WBEM and WMI classes have the syntax: "ns1/ns2/ns3:class" and the class it self can have base classes.
            # Survol classes have the syntax: "dir/dir/dir/class": This considers that namespaces are not really
            # necessary and can be replaced by classes. Also, there is a one-to-one match between the class inheritance
            # tree and its directory.
            # If Survol had to be started from scratch, there would be one Python class per survol class,
            # and they would be stored in the top dir "root/cimv2" ... it is not too late !
            #
            # This strips the upper directories: "mysql/instance" or "oracle/table", if this is a Survol class
            eltNam = subEntityGraphicClass.split("/")[-1]
            # This strips the namespace: "root/cimv2:CIM_LogicalElement", if this is a WBEM or WMI class.
            eltNam = eltNam.split(":")[-1]
            if not eltNam:
                # TODO: This is not the right criteria. Must select if we are listing scripts.
                eltNam = "script"

            eltNamPlural = lib_grammar.ToPlural(eltNam, numNodLst)
            txtElements = "%d %s" % (numNodLst, eltNamPlural)
            header = '<td border="1">' + lib_exports.DotBold(
                txtElements) + "</td>"

            # TODO: Replace each column name with a link which sorts the line based on this column.
            # The order of columns could be specified with an extra cgi argument with the columns names.
            for key in fieldsKeys:
                columnTitle = lib_kbase.qname(key, grph)
                columnTitle = columnTitle.replace("_", " ").capitalize()
                header += "<td border='1'>" + lib_exports.DotBold(
                    columnTitle) + "</td>"
            # With an empty key, it comes first when sorting.
            dictHtmlLines[""] = header

            # MAYBE SHOULD BE DONE TWICE !!!!! SEE ALSO ELSEWHERE !!!!
            subjUrlClean = subjUrl.replace('&', '&amp;')

            # BEWARE: The shape and the color of this HTML table is from the subjects,
            # because the elements can be of different classes, even if the share the same predicate.
            # TODO: Each row should have its own color according to its class.
            numFields = len(fieldsKeys) + 1

            # The rows of this HTML table could belong to different classes:
            # What the shared is the predicate. Hence, the predicate, property name is used as a title.
            propNamPlural = lib_grammar.ToPlural(propNam, None)
            helpText = "List of " + propNamPlural + " in " + labText

            # TODO: Le title and the content are not necessarily of the same class.
            # labTextWithBr is the first line of the table containing nodes linked with the
            # same property. Unfortunately we have lost this property.
            labText = lib_exports.TruncateInSpace(labText, 30)
            labTextWithBr = lib_exports.StrWithBr(labText)
            labTextWithBr += ": " + propNam

            if entity_id == "PLAINTEXTONLY":
                subjUrlClean = ""

            # This color is the table's contour.
            lib_patterns.WritePatterned(stream, subjEntityGraphicClass,
                                        subjNamTab, helpText, '"#000000"',
                                        subjUrlClean, numFields, labTextWithBr,
                                        dictHtmlLines)