This section describes the client-server architecture of the library.
The client-server architecture of Cogitant makes available objects handled by the library (support, graphs, rules) and operations (input / output, projection searches, rule applications, checkouts, etc.) to other applications, possibly run on other workstations. Uses of this client / server architecture can be divided into two categories:
In both cases, the server can be used as a support(s) and base(s) graphs server (graph storage and operation execution). In this section, we describe as a first step the compilation and installation procedure. In a second step we describe more precisely the classes of the client/server architecture. Finally, in the latter part, the exchange protocol is described, in the form of the format DTD.
A Cogitant server (called TCP server from now) may be queried by clients through a TCP connection (connected mode), or a message exchange following the HTTP protocol (each request makes a connection that is stopped after receiving the reply), as illustrated in Figure 1. In the first case, clients communicate directly with the TCP server. In the second case, clients communicate with a HTTP server (Apache http://www.apache.org for example), this HTTP server transmits its orders to the HTTP/TCP interfacing module of Cogitant, which is itself in communication with a TCP server. The interfacing module communicates with the TCP server through TCP, and it is therefore possible to install the TCP server on another machine (A) that the one used for the HTTP server (B). In this way, it is possible to install the TCP server on a machine only visible on a local area network (and invisible from the Internet), and to make this server (indirectly) available (to C, D, E and F) through a HTTP server, provided that the HTTP server machine, on which is executed the HTTP/TCP interfacing module, can access through TCP to the machine on which the TCP server is executed. HTTP/TCP interfacing is done through a CGI, which receives (by the POST protocol) data from the client. If you did not understand the preceding sentences, take an aspirin, and reread them, otherwise, an go on.
The library must be compiled with specific options so that client/server features are available. Exchanges between clients and server (exchange of XML messages, see Exchange protocol) can be done in different ways, but for now, the only exchange possibilities are the use of TCP or HTTP protocol. Exchange messages at the physical level (sockets) can be done in different ways, but for now, TCP and HTTP exchanges have been developed using the GNU Common C++ library (http://www.gnu.org/software/commoncpp). This library is a Free Software, offers a number of features that are not present in the C++ standard library. Nevertheless, it is not provided with standard compilers of the market. It is therefore required to download the source (http://www.gnutelephony.org/dist/tarballs/) and to compile it before being able to compile client/server functions of Cogitant. Note that in most GNU/Linux distributions, this library is available as a package. If you are in this case, it is not required to compile Common C++ from source, simply install the corresponding package as well as the development package (libcommonc++
and libcommonc++-dev
under Debian). Common C++ has been tested with Cogitant under Linux and Windows with version 1.7.3.
We assume the Common C++ library is installed ahead. To check this, simply type ccgnu-config –version
in a shell, the command must be recognized, and something like 1.9.7
should be displayed. If you are using version 2 of Common C++, you must use ccgnu2-config –version
. To compile Cogitant with client/server functions, you have to pass the –enable-cs
to the configure
script. As client/server functions can be developed with a library other than Common C++ (this is not the case now), it should be noted that the compiling will be done using the library, for this, you have to add the option –with-ccxx
. In the end, the call to the configure script must have this form:
After the configuration process, just launch the compiling in the usual way.
Note that using Borland C++ (recommended method in the 5.1.x versions of Cogitant) can not be used, it is now necessary to use Visual C++ to use the client-server architecture of Cogitant.
The first thing to do is the compiling of Common C ++ with Visual C ++. To do that, first download the sources of the library, and extract the contents of the archive. Thereafter, we assume that the archive has been extracted in D:\commoncpp2-1.x.x
. Open the project D:\commoncpp2-1.x.x\w32\vs2008\ccgnu2.vcproj
. Ensure that Visual C++ generate files in Release mode (abd not in Debug mode) and launch the compiling. Once the compiling is finished D:\commoncpp2-1.x.x\w32\vs2008\Release
contains the two dynamic libraries (CapeCommon17.dll
and CapeExtras17.dll
, but this may vary depending on the version of Common C++).
No it is time to configure Cogitant, in the usual way, with CMake. Launch CMake on the Cogitant directory and, after selecting file generation for Visual C++ 2008, check WithClientServer_CommonCPP. Click on Configure. An error occurs, but it is quite normal: you must choose in CcppBaseDir the directory that contains Common C++, in our example D:\commoncpp2-1.x.x
. CcppLibraryDir (directory that contains Common C++ dlls)should be set automatically after a Configure and also CcppLibraries (names of Common C++ dlls, without "dll" suffix). If not, change the values of these variables. Click on Generate and open the project in Visual C++ to start the compiling.
To check that the library has been properly compiled with the client/server features enable, the easiest way is to run the example of server and client. The server is samples/doc/server_1[.exe]
and the client is in test/client
. Both programs are compiled by default (at the make
time) under Unix, and must be compiled (in the usual way) under Windows. Run the server, then launch the client. This client is testing the server by sending it a large number of requests by TCP. Under Windows, make sure dynamic libraries (dll) required for the execution of these programmes are in search paths: cogitant.dll
(if Cogitant was compiled in dll), Common C++ dlls, and runtime libraries from the compiler. For this, you can copy the these dll into directories containing executables, or change the value of PATH
so that it contains directories containing dlls in question.
You can connect to the server by using the telnet application. To do so, execute the command telnet 127.0.0.1 4246
(4246 is the port used by the default server). You can then enter queries respecting the Exchange protocol. The server response is displayed in return.
No installation is required. You just have to compile the server. The server code (server_1.cpp
) is very simple, it merely instanciates classes of the library. The only important point lies in the choice of operations that are offered to clients. So you can build a server precisely providing the operations you want to provide (and prohibit others, for example, you prohibit the clients to load a new support, or to load graphs, etc.). By taking inspiration from the source of the default server, you can also build a server providing new operations, for this you just have to define new subclasses of cogitantcs::OpeServer and to make these operations available to the server.
The use of the HTTP protocol is recommended when the Cogitant server has to be available via the Internet. Indeed, in the case of using HTTP, you just have to install the interfacing module HTTP/TCP to make it known by the HTTP server (Apache, etc.), and in this way, the Cogitant server is accessible through the HTTP server, like any Web page. Thus, messages can pass through a firewall (as the port 80 used by the HTTP service is not usually filtered), and there is no need to "open" a new port to pass messages as would ask the TCP server.
To make the Cogitant server accessible via HTTP, it is therefore required to install the CGI of TCP/HTTP interfacing on the machine running the HTTP server. CGIs are often written in script languages (it would be possible to use such a script here), but the interfacing module is written in C++, even if its code is very simple: it merely "redirects" the request (received by POST) to the TCP server (by default, it forwards the request on the TCP server running on the same machine, but you just have to modify the source (cgtserver_cgi.cpp
) to redirect the request to a TCP server of another machine). The source of this CGI resides in the directory samples/cgiserver
.
In this directory, there is also a file query.html
which can be used to send a request to a server Cogitant from a browser. To use this html page, the cgi must be executable by the HTTP server in the same directory (see below).
cogitant/samples/cgiserver
accessible by the Apache server, and then you can view the page. /var/www
(or /var/www/html
) is the directory which must contain files and directories served by Apache, you can make the directory cogitant/samples/cgiserver
visible from the server as being /cogitant
, by typing the command ln -s /monrepertoire/sources/cogitant-5.x.x/samples/cgiserver /var/www/cogitant
(or /var/www/html/cogitant
). Check settings of Apache (/etc/apache/httpd.conf
or /etc/httpd/conf/httpd.conf
) that the Apache server is allowed to follow symlinks (section Directory "var/www/[html/]"
option FollowSymLinks
) and that cgi are executable in directories containing pages (option ExecCGI
). The above method only works under Unix. The method described here works under Unix and Windows. This method ask to create a new alias. Edit the configuration file of Apache httpd.conf
, located in /etc/apache
or /etc/httpd/conf
under Unix and Apache/conf
under Windows, and add into the section IfModule mod_alias.c
a new alias (change the path given by the path in which resides cgtserver.cgi
on your system). This method has the advantage of not asking to add the option ExecCGI
to directories containing HTML files (but only the directory containing the CGI of the interfacing module). Under Unix:
If not done yet, configure Apache to make it recognize the extension cgi
for CGIs: In section IfModule mod_mime.c
, add (or uncomment) the line
If you do not want to allow .cgi
files to be executed by the server, you can simply rename cgtserver.cgi
in a name being recognized as cgi by the server (exe
for example). In this case, it is obviously necessary to modify the html page. Finally, in order to make the cgi being executed correctly, it is necessary, under Windows, to copy into the directory cgiserver
dlls that are essential to the execution: cogitant.dll
, ccgnu2.dll
, cc3250mt.dll
. If one of these dlls is missing, accessing the cgi causes the display of the page Internal server error (not serious, but the request cannot be sent to the TCP server, because the CGI can not be executed).
Start the TCP server (in samples/doc
), by typing server_1
. It is then possible to load the test page in a browser (http://127.0.0.1/cogitant/query.html
). If you do not see the page, you badly configured the alias (thought about restarting apache?) or the link.
The execution of a request should provoke a response (or an error if the request does not match the dtd cogitantcs.dtd
, but not an error of type unknownserver
, indicating that the HTTP/TCP interfacing module did not find the TCP server). Note that the server is expected to communicate with clients which respect the DTD, and that therefore, if incorrect request are sent to the server, this can lead to strange behaviors or even a server freeze. Note that most browsers (Mozilla, IE, etc.) do not display XML documents in the usual area, and that one must choose the source display to see the server response. You can also use the test client application, located in test/client
and called client
, which communicates with the server by TCP or HTTP. You can use parameters passed to the program to specify a machine name, a port number, the use of the HTTP protocol and the URL (syntaxe : client [http] [http://<host>[:<port>]/<dir>/cgtserver.cgi] [tcp [<host_tcp>[<port_tcp>]]]
).
Classes enabling exchange between server and client are all in the namespace cogitantcs. They enable to use remote (on the server) supports, graphs and rules as if they were local (on the client). Once a distant object is needed on the client, a request is sent by the client, and the server returns it characteristics of this object. See examples (samples/doc/server_*.cpp
) and documentation of classes in the namespace cogitantcs for more information.
The server management is provided by the class cogitantcs::Server. This class manages the receipt of client messages, the execution of requests and the sending of replies. Each server is associated with a set of cogitant::Environment, and this is the content of these environments (support, graphs and rules as well as the corresponding operations) which is offered to clients. To adapt a server to different uses, it is possible to customize certain aspects of the server, like operations available and offered to the server and the mode of receipt and sending.
In general, the server execution is launched by instantiating cogitantcs::Server (or a subclass, see below), calling the method cogitantcs::Server::addStdOperations() or cogitantcs::Server::addMinOperations(), and finally calling cogitantcs::Server::mainLoop().
The method addStdOperations() can launch a server that offers all standard operations provided with the library, while the method addMinOperations() creates a server that merely offers minimal operations, i.e. those for accessing (read only) supports and graphs of the server. In the latter case, clients cannot modify the provided data, it is therefore necessary, before calling mainLoop(), to build one (or more) environment. To do this, simply initialize an environment in the usual way, then add this environment to the server by calling cogitantcs::Server::addEnvironment() (see example below).
In most cases, it is necessary to add new operations to servers, for example, specific operations for questioning of a graph, or specific operations of input output. Such operations are not part of the "standard" protocol of Cogitant but can be used by a "specific" client to query a "specific" server. Like any Cogitant operations, such operations must be subclasses of cogitant::OperationBase. Actually, server operations must be subclasses of cogitantcs::OpeServer. By using this mechanism, it is not necessary to write a subclass of Server to add new operations, just write a subclass of OpeServer corresponding to the desired operation, instantiate this class, and add this instance to the server using the method cogitantcs::Server::addOperation(). In this way, adding new operations, or modifying existing operations is simplified, and can even be done dynamically, during the execution of the server. To define a new operation it is therefore necessary to define a subclass of cogitantcs::OpeServer. More precisely, it is necessary to redefine two methods in the subclass to be created:
cogitantquery
of level 0, which contains tags of calls to operations, each of these tags having as a name the name of the operation to be executed (prefixed by q
as query, see Exchange protocol). So, for a request qloadgraphs
, the server will automatically execute the operation for which the method name() returns loadgraphs
. While it is possible to customize the functioning of a server by adding new operations, it is also possible to customize it by using special functions of XML messages transport. Indeed, depending on the application, communication between client and server can be done in different ways. Cogitant comes with a server that can be questioned through a TCP connection (with Common C++) and a client can connect to the server via a TCP or HTTP connection (still with Common C++). It is possible to add new transport methods, for example to use libraries different from Common C++ or by directly "tackling" the sockets layer of the operating system, or to use other communication methods that enable transporting XML documents (such as pipes, or name pipes). Adding new transport methods is very easy, because transport operations are not part of the server or the client, but are deported in the abstract class cogitantcs::OperationCSIO (client server input output). To any server and to any client is associated with an instance of a subclass of cogitantcs::OperationCSIO. And each time the client or the server must send data, receive data, or pending connections, it calls methods on this instance of OperationCSIO). So, to define new methods of information transport, just define a subclass of cogitantcs::OperationCSIO and more precisely redefine the following methods:
For example, cogitantcs::OperationCSIO_Simple provides input output operations from C++ flows. The constructor of this class takes two parameters: a ostream and a istream. Using this input output operation, you can for example use a server on cout
and cin
, pass requests on the standard input, and retrieve results on the standard output. However this operation is not cannot be used for real communication with a client. For real connections, on can use the class cogitantcs::OperationCSIO_TCP which use Common C++ to provide inputs/outputs on TCP. This class is provided with two constructors cogitantcs::OperationCSIO_TCP::OperationCSIO_TCP(), the first only takes an integer parameter, and should be used to instantiate the operation to be used in a server. The integer is thus the waiting port number of the server. The second constructor takes two parameters: a string and an integer. The second has to be used in a client. The first will be the server name to which the client will connect, and the second parameter the port number. To use a specific class of transport, simply instantiate the chosen class, and pass a pointer to this instance to the constructor of cogitantcs::Server or cogitantcs::Client. It is possible to do the same thing more simply, by using the class cogitantcs::Server_TCP whose constructor only takes a parameter, the waiting port number (see the example above). Note however that the use of Server_TCP is merely a shortcut and is exactly equivalent to instantiating cogitantcs::OperationCSIO_TCP and pass the created instance to the constructor cogitantcs::Server::Server().
The client (cogitantcs::Client) can be used in different ways, more or less simple and allow access to more or less features.
Of course, these three use modes correspond to a single method of communication with the server, the exchange of XML messages using the transport layer.
As in the case of a server, the transport of XML messages is carried out by a subclass of cogitantcs::OperationCSIO. The Client constructor takes as a parameter a pointer to an instance of a subclass of OperationCSIO. However, in order to ease the use of a TCP client, the class cogitantcs::Client_TCP is defined, and its constructor takes two parameters: the hostname on which the server is executed, and the waiting port number of the server. Similarly, the class cogitantcs::Client_HTTP allows you to simply connect to a server via HTTP. The constructor of this class only takes a single parameter, a string which should contain the URL of the interfacing CGI.
To use a client, one should not instantiate the class cogitant::Environment, because the client requires the use of specific data structures. These classes (cogitantcs::SetClient and its subclasses, cogitantcs::EnvironmentAllocatorClient) provide sets (of types, of graphs) which query the server as soon as a required element is unknown to the client. So, when a client connects to a server, it does not transfer the whole support and graphs, but objects are transferred as soon as necessary. One sould not instantiate Environment, but rather the method cogitantcs::Client::environment() which returns (a pointer to) the environment managed by the client. But this environment cannot be used from the connection to the server. Indeed, a server can handle multiple environments, it is therefore necessary to select which of its environnements is chosen by the client. This is done thanks to the method cogitantcs::Client::setServerEnvironment(). This method is overloaded: it may take as a parameter an integer (server-side environment number, i.e. the result of cogitantcs::Server::addEnvironment()) or a string (name of the environment , i.e. the string passed as a second parameter to cogitantcs::Server::addEnvironment()). So the example below connects to a server and selects the environment named env_bucolic
.
As can be seen from this example, once the client is initialized, it is possible to access objects in the environment in the usual way. Thus, the execution of this programme (whose result is given below) displays the support and the first graph (in the linear format), the same way as if this support and this graph were "locally" loaded.
cogitant::Support ConceptTypes : 0:Universal, 1:Action, 2:Attribute, 3:Boat, 4:Bucolic, 5:Couple, 6:Entity, 7:Fish, 8:Lake, 9:Living being, 10:Painting, 11:Person, 12:Place, 13: Scene, 14:Sleep, 15:Think PartialOrder : (0 (1, 2, 6)) (1 (15, 14, 7)) (2 (4)) (3 ()) (4 ()) (5 ()) (6 (1 3, 5, 3, 12, 10, 9)) (7 ()) (8 ()) (9 (11, 7)) (10 ()) (11 ()) (12 (8)) (13 ()) (14 ()) (15 ()) RelationTypes : 0:agent, 1:attr, 2:in, 3:object, 4:on PartialOrder : (0 ()) (1 ()) (2 ()) (3 ()) (4 ()) NestingTypes : 0:Description, 1:Component, 2:Representation PartialOrder : (0 (1, 2)) (1 ()) (2 ()) Individuals : 0:Peter(11), 1:A(10) sleepandfish: [Person:Peter]<-(agent)<-[Sleep] [Person]<-(agent)<-[Fish]->(in)->[Lake].
Once an access will be done on an environment object which is not present in the client, a request will be automatically generated towards the server, which return the object description to the client. The client environment is therefore an image of the server environment, this image can be incomplete as some objects have not (yet) been transferred. This has a consequence, however: it is forbidden to add or delete objects in the client environment, because in this case, the client environment would no longer be an image of the server. Moreover, in the case where several clients are connected to the same server, it would be very difficult and costly to maintain consistency between all environments. That is why automatic transfers are done in a server to client direction, and the server environment is evidence.
However, in some cases, it may be necessary, from a client, to modify server objects (modifying an existing graph, adding or deleting a graph, loading a support, etc.). For such uses, the class cogitantcs::Client contains methods like cogitantcs::Client::newEnvironmentObject() that can change the server environment (and thus, indirectly, the client environment). It is then possible to access functions that are not available from the environment. For example, if a client-side graph is changed, these changes are not sent to the server because made they are done onto the local copy. By using the method cogitantcs::Client::commitEnvironmentObject(), it is possible to transfer modifications to the server. The corresponding sever-side graph is then changed, provided that the server accepts modifications from clients. Such a modification of the server environment is not automatic: it is necessary to explicitly call this method. So, concurrence issues (updating a single object by two clients connected to the same server for example) are the responsibility of the programmer. Refer to the documentation of cogitantcs::Client to know all methods interacting with the server.
In some cases, it is better to have no mechanism for automatic updates, and to explicitely make all updates, whether in one way or another. Among other drawbacks of the automatic update, graphs have the same identifier (iSet) in the server and the client, which prevents to create new ones or to destroy some in the client (except via methods of access to the server, which may in some cases become weighty). In the case where the client only needs a few graphs, it may be easier to disable the automatic update, to retrieve some graphs from the server (which have in the client an identifier which may be different from their identifier in the server), to create some locally or load some from a local file, then to send some of these graphs the server to make it perform certain processings. It is possible to disable automatic updates separately for the support and for environment objects. This requires using the two last parameters of the constructor of cogitantcs::Client (and its sub-classes, like cogitantcs::Client_TCP::Client_TCP()), as described in the two examples below.
Example. The example below creates a client in which support and graphs are different from those handled by the server. In this way, the client can modify "its" support (by loading a file using cogitant::Environment::readSupport:()) and send the support got from the server (cogitantcs::Client::commitSupport()). On the server side, the environment created for the occasion (cogitantcs::Client::newEnvironment()) receives the server sent by the client. To check it, a second client is created and chooses the environment previously created on the server. So by displaying the support associated with this customer, we get the support that was sent by the first client.
Example. The second example illustrates the use of a support obtained from the server, and graphs (and rules) local to the client. Here, the client load (locally) a graph and a rule, and sends these two objects to the server (cogitantcs::Client::commitEnvironmentObject()), after obtaining two identifiers from the server to store these two objects (cogitantcs::Client::newEnvironmentObject()). The graph loaded from the client and sent to the server is then stored on the server side (cogitantcs::Client::saveEnvironmentObject()). The second client enables to check the result of operations of the first: the graph and the rule are available on the server.
Finally, the third mode of exchange with the server is to explicitely generate an request in the form of an XML document. Indeed, methods of cogitantcs::Client cover only "standards" messages of Cogitant. If new operations are defined in the server, it is necessary to send specific messages. To do this, you can use cogitantcs::Client::addPendingQuery() which adds to the "pending" XML document an operation execution tag and cogitantcs::Client::answer() which returns the XML response received from the server.
It is also possible to send XML messages from an application that is not based on Cogitant, for example to use functions offered by a server from a Java application. The example below, to be used in conjunction with the server server_1
, can create an environment, load a graph and send it to the client from a java client which merely sends and receive XML messages.
The above example is very simplied, as responses received from the server are not analysed, and are displayed as they are on the screen. However, there are a large number of Java libraries that can parse XML, and by using them, you can easily interpret server responses, to determine whether the graph was correctly loaded, what are the vertices that form the graph and to build a representation "local to the client" of the graph, and so on.
Exchanges between server and client are done though XML messages. The following DTD documents messages that can be sent by the client and by the server. The client can only send documents having a tag cogitantquery
as a root, and the server documents having cogitantanswer
as a root.
<?xml version="1.0" encoding="UTF-8"?> <!-- DTD CogitantCS 1.3 This DTD describes the exchange protocol between a Cogitant server and its clients. To apply this DTD, use the following syntax: <!DOCTYPE cogitantquery PUBLIC "-//COGITANT//Cogitant Client-server Specification 1.3//EN" "http://cogitant.sourceforge.net/cogitantcs.dtd"> or <!DOCTYPE cogitantanswer PUBLIC "-//COGITANT//Cogitant Client-server Specification 1.3//EN" "http://cogitant.sourceforge.net/cogitantcs.dtd"> This file is part of Cogitant which is a library facilitating construction of applications using conceptual graphs. It is under GPL licence. http://cogitant.sourceforge.net Cogitant version 5.2.0 - Last change of the DTD : 21/05/2009 --> <!-- Extensions des attributs des balises standard. --> <!ENTITY % cogitantQueryExtensions ""> <!ENTITY % cogitantAnswerExtensions ""> <!-- Document. --> <!ELEMENT cogitantquery (qserver | qenvironment | qsetcontent | qconcepttype | qrelationtype | qnestingtype | qindividual | qenvironmentobject | qgraph | qrule | qcommitenvironmentobject | qcommitimmediateless | qcomparison | qimmediateless | qimmediategreater | qbannedobjects | qgraphobject qnewenvironment | qnewenvironmentobject | qloadsupport | qsavesupport | qloadgraphs | qsaveenvironmentobjects | qdeleteenvironment | qdeleteenvironmentobject | qnewsupportobject | qcommitsupportobject | qprojections | qruleapplications | qruleapply | qrulesclosure | qconstraintsatisfaction | qcopyenvironmentobject | qoperationconfig | qaddconcept | qaddrelation | qaddnesting | qdelgraphobject | qaddedge | qdeledge | %cogitantQueryExtensions; )*> <!-- reguiresheader : si égal à 0, le serveur n'envoie pas une entête destinée au serveur http (Content-type: ... Cet attribut doit donc être utilisé (avec la valeur 0) quand le serveur est utilisé en accès direct (TCP) pour ne pas recevoir ce header. --> <!ATTLIST cogitantquery requiresheader CDATA '1' > <!ELEMENT cogitantanswer (server | environment | setcontent | concepttype | relationtype | nestingtype | individual | environmentobject | graph | rule | deletedobject | commitenvironmentobject | commitimmediateless | comparison | immediateless | immediategreater | bannedobjects | graphobject newenvironment | newenvironmentobject | loadsupport | loadgraphs | saveenvironmentobjects | deleteenvironment | deleteenvironmentobject | newsupportobject | commitsupportobject | projections | ruleapplications | ruleapply | rulesclosure | constraintsatisfaction | copyenvironmentobject | operationconfig | addconcept | addrelation | addnesting | delgraphobject | addedge | deledge | error %cogitantAnswerExtensions;)*> <!ATTLIST cogitantanswer > <!ELEMENT error EMPTY> <!ATTLIST error <!-- syntax : syntaxe xml non respectée dans le message requête. environmentid : identificateur d'environnement inconnu (attribut env). setid : identificateur d'ensemble inconnu (attribut set). objectid : identificateur d'objet inconnu (par ex, dans un graphe, une arête a pour extrémité un objet qui n'existe pas). objecttype : l'identificateur d'objet se réfère à un objet du mauvais type (par ex, vérification d'une contrainte avec l'id d'un objet de l' environnement qui n'est pas une contrainte. unknownserver : retourné par cgiserver quand il n'arrive pas à établir une connexion avec le serveur TCP. arg : contient un descriptif plus détailé de l'erreur (parfois). --> type (syntax | environmentid | setid | objectid | objecttype | unknownserver) #REQUIRED arg CDATA #IMPLIED > <!-- Propriétés d'un objet --> <!ELEMENT properties (property*)> <!-- subid : Identificateur de sous-ensemble de propriétés. Si absent, l'élément prop désigne l'ensemble principal. --> <!ATTLIST properties subid CDATA #IMPLIED > <!ELEMENT property EMPTY> <!ATTLIST property type CDATA #IMPLIED value CDATA #IMPLIED > <!-- A partir de la DTD 1.3, certains attributs d'objets ne sont plus transférés comme des propriétés mais des attributs. Les attributs correspondent à des informations faisant partie du modèle (conformité d'un type de relation, par exemple), alors que les propriétés représentent des informations externes au modèle (position d'un sommet, par exemple). --> <!ELEMENT attribute EMPTY> <!ATTLIST attribute type CDATA #IMPLIED value CDATA #IMPLIED > <!-- Identificateur d'un objet de l'environnement. --> <!ELEMENT eoid EMPTY> <!ATTLIST eoid id CDATA #REQUIRED > <!-- Serveur. --> <!ELEMENT qserver EMPTY> <!ATTLIST qserver> <!ELEMENT server (properties*)> <!-- environments : Nombre d'environnements disponibles sur le serveur --> <!ATTLIST server environments CDATA #REQUIRED > <!-- Environnement. --> <!ELEMENT qenvironment EMPTY> <!-- env (numéro de l'environnement) ou name doivent être donnés. --> <!ATTLIST qenvironment env CDATA #IMPLIED name CDATA #IMPLIED > <!ELEMENT environment (properties*, support)> <!-- objects : Taille de l'ensemble d'EnvironmentObjects --> <!ATTLIST environment env CDATA #REQUIRED objects CDATA #REQUIRED name CDATA #IMPLIED > <!ELEMENT support (properties*)> <!-- xxxtypes : Tailles des ensembles du support --> <!ATTLIST support concepttypes CDATA #REQUIRED relationtypes CDATA #REQUIRED nestingtypes CDATA #REQUIRED individuals CDATA #REQUIRED bannedtypes CDATA #REQUIRED > <!-- Contenu d'un ensemble (de TC, de TR, de TN, de MI, de EnvironmentObject). --> <!ELEMENT qsetcontent EMPTY> <!-- set : Numéro de l'ensemble = numéro de l'environnement * 10 +1 (TC) +2 (TR) +3 (TN) +4 (MI) +5 (EnvironmentObjects). id : Identificateur dans l'ensemble de l'élément demandé ou "all" : tout le contenu de l'ensemble. --> <!ATTLIST qsetcontent set CDATA #REQUIRED id CDATA #REQUIRED > <!-- Type de concept. --> <!ELEMENT qconcepttype EMPTY> <!-- env : Numéro de l'environnement. (idem relationtype, nestingtype, individual, environmentobject) id : Numéro dans l'ensemble de l'objet demandé ou "all" (tout le contenu de l'ensemble). (idem relationtype, nestingtype, individual, environmentobject) --> <!ATTLIST qconcepttype env CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT concepttype (attribute*, properties*)> <!ATTLIST concepttype set CDATA #REQUIRED id CDATA #REQUIRED > <!-- Type de relation. --> <!ELEMENT qrelationtype EMPTY> <!ATTLIST qrelationtype env CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT relationtype (attribute*, properties*)> <!ATTLIST relationtype set CDATA #REQUIRED id CDATA #REQUIRED > <!-- Type d'emboîtement. --> <!ELEMENT qnestingtype EMPTY> <!ATTLIST qnestingtype env CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT nestingtype (attribute*, properties*)> <!ATTLIST nestingtype set CDATA #REQUIRED id CDATA #REQUIRED > <!-- Marqueur individuel. --> <!ELEMENT qindividual EMPTY> <!ATTLIST qindividual env CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT individual (attribute*, properties*)> <!ATTLIST individual set CDATA #REQUIRED id CDATA #REQUIRED > <!-- Élément de l'environnement. --> <!ELEMENT qenvironmentobject EMPTY> <!-- env : Numéro de l'environnement côté serveur. id : Numéro de l'objet dans l'environnement ou "all". iddest : Numéro d'identifiant chez le client. N'est précisé que dans le cas où il est différent d'id. La valeur de cet attribut est simplement recopiée telle quelle par le serveur dans la réponse, et peut être utilisée par le client. Cet attribut ne doit pas être utilisé dans le cas ou id vaut "all". --> <!ATTLIST qenvironmentobject env CDATA #REQUIRED id CDATA #REQUIRED iddest CDATA #IMPLIED > <!ELEMENT deletedobject EMPTY> <!ATTLIST deletedobject set CDATA #REQUIRED id CDATA #REQUIRED > <!-- Graphe. --> <!ELEMENT qgraph EMPTY> <!-- cf. environmentobject --> <!ATTLIST qgraph env CDATA #REQUIRED id CDATA #REQUIRED iddest CDATA #IMPLIED > <!ELEMENT graph (properties*, (internalgraph | concept | relation | nesting | coreferenceclass)*)> <!-- size : Taille de l'ensemble de noeuds. set, id : sont obligatoires quand le graphe est transféré en tant qu'objet de l'environment, sont interdits quand le graphe est transféré à l'intérieur d'une règle. --> <!ATTLIST graph set CDATA #IMPLIED id CDATA #IMPLIED iddest CDATA #IMPLIED size CDATA #REQUIRED > <!ELEMENT internalgraph (properties*, edge*)> <!ATTLIST internalgraph id CDATA #REQUIRED > <!ELEMENT concept (properties*, edge*)> <!ATTLIST concept id CDATA #REQUIRED > <!ELEMENT relation (properties*, edge*)> <!ATTLIST relation id CDATA #REQUIRED > <!ELEMENT nesting (properties*, edge*)> <!ATTLIST nesting id CDATA #REQUIRED > <!ELEMENT coreferenceclass (properties*, edge*)> <!ATTLIST coreferenceclass id CDATA #REQUIRED > <!-- Attention, l'ordre des arêtes liées à un noeud d'un graphe est important, et doit être transféré "tel quel". --> <!ELEMENT edge EMPTY> <!-- env : Extrémité de l'arête (identificateur de sommet, ou * pour ISET_NULL) label : Étiquette de l'arête (entier (étiquette de l'arête) ou 'P' (lien parent) 'C' (lien enfant) ou '=' (lien de coreference). --> <!ATTLIST edge end CDATA #REQUIRED label CDATA #REQUIRED > <!-- Règle. --> <!ELEMENT qrule EMPTY> <!-- cf. environmentobject --> <!ATTLIST qrule env CDATA #REQUIRED id CDATA #REQUIRED iddest CDATA #IMPLIED > <!ELEMENT rule (properties*, hypothesis, conclusion, conpt*)> <!ATTLIST rule set CDATA #IMPLIED id CDATA #REQUIRED iddest CDATA #IMPLIED > <!ELEMENT hypothesis (graph)> <!ATTLIST hypothesis> <!ELEMENT conclusion (graph)> <!ATTLIST conclusion> <!ELEMENT conpt EMPTY> <!ATTLIST conpt idc1 CDATA #REQUIRED idc2 CDATA #REQUIRED > <!-- Transfert d'un objet de l'environnement du client vers le serveur. --> <!ELEMENT qcommitenvironmentobject (graph | rule)> <!ATTLIST qcommitenvironmentobject env CDATA #REQUIRED > <!ELEMENT commitenvironmentobject EMPTY> <!ATTLIST commitenvironmentobject error CDATA #IMPLIED> <!-- Accès aux plus petits immédiats d'un type. --> <!ELEMENT qcommitimmediateless (qilid*)> <!-- tp= "c": type de concept, "r": type de relation, "n": type d'emboîtement --> <!ATTLIST qcommitimmediateless env CDATA #REQUIRED tp CDATA #REQUIRED > <!ELEMENT qilid EMPTY> <!-- id1 > id2 setunset= "s" fixer id1 > id2, "u" supprimer id1 > id2 --> <!ATTLIST qilid id1 CDATA #REQUIRED id2 CDATA #REQUIRED setunset CDATA 's' > <!ELEMENT qcommitimmediateless> <!ATTLIST qcommitimmediateless error CDATA #IMPLIED> <!-- Comparaison de deux types. --> <!ELEMENT qcomparison EMPTY> <!-- set : Numéro de l'ensemble = numéro de l'environnement * 10 +1 (TC) +2 (TR) +3 (TN). --> <!ATTLIST qcomparison set CDATA #REQUIRED id1 CDATA #REQUIRED id2 CDATA #REQUIRED > <!ELEMENT comparison EMPTY> <!ATTLIST comparison set CDATA #REQUIRED id1 CDATA #REQUIRED id2 CDATA #REQUIRED result CDATA #REQUIRED > <!-- Accès aux plus petits immédiats d'un type. --> <!ELEMENT qimmediateless EMPTY> <!-- set : Numéro de l'ensemble = numéro de l'environnement * 10 +1 (TC) +2 (TR) +3 (TN). id : identificateur du type ou "all". --> <!ATTLIST qimmediateless set CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT immediateless (ilid*)> <!ATTLIST immediateless set CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT ilid EMPTY> <!ATTLIST ilid id CDATA #REQUIRED > <!-- Accès aux plus grands immédiats d'un type. --> <!ELEMENT qimmediategreater EMPTY> <!-- set : Numéro de l'ensemble = numéro de l'environnement * 10 +1 (TC) +2 (TR) +3 (TN). id : identificateur du type ou "all". --> <!ATTLIST qimmediategrater set CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT immediategreater (igid*)> <!ATTLIST immediategreater set CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT igid EMPTY> <!ATTLIST igid id CDATA #REQUIRED > <!-- Accès aux types interdits. --> <!ELEMENT qbannedtypes EMPTY> <!-- env : identificateur de l'environnement à interroger. --> <!ATTLIST qbannedtypes env CDATA #REQUIRED> <!ELEMENT bannedtypes (bannedtype*)> <!ATTLIST bannedtypes env CDATA #REQUIRED> <!-- L'élément bannedtypes contient autant d'éléments bannedtype qu'il y a de types interdits. --> <!ELEMENT bannedtype (btid*)> <!-- Chaque élément bannedtype contient des éléments btid pour chaque identificateur de type primitif composant le type conjonctif interdit. --> <!ELEMENT btid EMPTY> <!ATTLIST btid id CDATA #REQUIRED > <!-- Accès à un objet (concept, relation, graphe interne, emboîtement, classe de coref) composant un graphe. --> <!ELEMENT qgraphobject EMPTY> <!-- env : Numéro de l'environnement côté serveur. idgraph : Identificateur du graphe. idobject : Identificateur de l'objet l'intérieur du graphe. --> <!ATTLIST qgraphobject env CDATA #REQUIRED idgraph CDATA #REQUIRED idobject CDATA #IMPLIED > <!ELEMENT graphobject (internalgraph | concept | relation | nesting | coreferenceclass)> <!ATTLIST graphobject env CDATA #REQUIRED idgraph CDATA #REQUIRED > <!-- Création d'un nouvel environnement. --> <!ELEMENT qnewenvironment EMPTY> <!ATTLIST qnewenvironment <!-- Optimisation automatique de l'ordre partiel des types de concepts. Par défaut, oui. Attention, s'il est optimisé, il ne doit plus être modifié. --> optorderc CDATA "true" <!-- Idem, sur les types de relations. --> optorderr CDATA "true" <!-- Idem, sur les types d'emboîtements. --> optordern CDATA "true" <!-- Optimisation automatique pour la recherche rapide de types de concepts par leur intitulé. --> optlabelc CDATA "true" <!-- Idem, sur les types de relations. --> optlabelr CDATA "true" <!-- Idem, sur les types d'emboîtements. --> optlabeln CDATA "true" <!-- Idem, sur les marqueurs individuels. --> optlabeli CDATA "true" > <!ELEMENT newenvironment EMPTY> <!ATTLIST newenvironment env CDATA #REQUIRED > <!-- Création d'un nouvel objet dans l'environnement. --> <!ELEMENT qnewenvironmentobject EMPTY> <!-- tp= "g": graphe (par défaut), "r": règle, "p": contrainte positive, "n": contrainte négative. --> <!ATTLIST qnewenvironmentobject env CDATA #REQUIRED tp (g | r | p | n) "g" > <!ELEMENT newenvironmentobject EMPTY> <!ATTLIST newenvironemntobject id CDATA #REQUIRED > <!-- Chargement d'un support. --> <!-- Le support peut être chargé à partir d'un fichier disponible sur l'hote serveur, en utilisant l'attribut file. Le support peut aussi être transféré à partir du client. Dans ce cas, l'attribut file ne doit pas être donné, et l'attribut embedded doit valoir "bcgct" ou "cogxml" ou "cgif". La balise qloadsupport doit alors avoir comme fils le texte bcgct ou cogxml du support. --> <!ELEMENT qloadsupport (#PCDATA)> <!ATTLIST qloadsupport env CDATA #REQUIRED file CDATA #IMPLIED embedded CDATA #IMPLIED > <!ELEMENT loadsupport EMPTY> <!ATTLIST loadsupport error CDATA #IMPLIED > <!-- Sauvegarde du support côté serveur. Si l'attribut embedded est passé, il doit valoir "bcgct", "cogxml" ou "cgif". Dans ce cas, l'attribut "file" ne doit pas être passé, et le resultat de l'écriture dans le format choisi n'est pas enregistré sur disque, mais transmis dans la réponse saveenvironmentobjects comme texte emboîté (dans un PCDATA). --> <!ELEMENT qsavesupport EMPTY> <!ATTLIST qsavesupport env CDATA #REQUIRED file CDATA #IMPLIED embedded CDATA #IMPLIED > <!ELEMENT savesupport (#PCDATA)> <!ATTLIST savesupport error CDATA #IMPLIED > <!-- Chargement de graphes (ou règles). Transfert d'un graphe en PCDATA par utilisation de l'attribut embedded. --> <!ELEMENT qloadgraphs (#PCDATA)> <!ATTLIST qloadgraphs env CDATA #REQUIRED file CDATA #IMPLIED embedded CDATA #IMPLIED > <!ELEMENT loadgraphs (loadedgraph*)> <!ATTLIST loadgraphs error CDATA #IMPLIED > <!ELEMENT loadedgraph EMPTY> <!ATTLIST loadedgraph id CDATA #REQUIRED > <!-- Sauvegarde d'un graphe (ou règle). Si l'attribut embedded est passé, il doit valoir "bcgct", "cogxml" ou "cgif". Dans ce cas, l'attribut "file" ne doit pas être passé, et le resultat de l'écriture dans le format choisi n'est pas enregistré sur disque, mais transmis dans la réponse saveenvironmentobjects comme texte emboîté (dans un PCDATA). --> <!ELEMENT qsaveenvironmentobjects (eoid*)> <!ATTLIST qsaveenvironmentobjects env CDATA #REQUIRED file CDATA #IMPLIED id CDATA #IMPLIED > <!ELEMENT saveenvironmentobjects (#PCDATA)> <!ATTLIST saveenvironmentobjects error CDATA #IMPLIED > <!-- Destruction d'un environnement. --> <!ELEMENT qdeleteenvironment EMPTY> <!ATTLIST qdeleteenvironment env CDATA #REQUIRED > <!ELEMENT deleteenvironment EMPTY> <!ATTLIST qdeleteenvironment > <!-- Destruction d'un objet de l'environnement --> <!ELEMENT qdeleteenvironmentobject EMPTY> <!ATTLIST qdeleteenvironmentobject env CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT deleteenvironmentobject EMPTY> <!ATTLIST deleteenvironmentobject error CDATA #IMPLIED > <!-- Création d'un nouveau type ou marqueur --> <!ELEMENT qnewsupportobject EMPTY> <!-- tp= "c": type de concept, "r": type de relation, "n": type d'emboîtement, "i": marqueur individuel. Le label est l'intitulé (optionnel) de l'élément à créer. Si aucun intitulé n'est passé, utiliser qcommitsupportobject pour envoyer la propriété label. --> <!ATTLIST qnewsupportobject env CDATA #REQUIRED tp CDATA #REQUIRED label CDATA #IMPLIED > <!ELEMENT newsupportobject EMPTY> <!ATTLIST newsupportobject id CDATA #REQUIRED > <!-- Transfert d'un objet du support (TC, TR, TN, I) du client vers le serveur. --> <!ELEMENT qcommitsupportobject (properties*)> <!ATTLIST qcommitsupportobject env CDATA #REQUIRED tp CDATA #REQUIRED id CDATA #REQUIRED > <!ELEMENT commitsupportobject EMPTY) <!ATTLIST commitenvironmentobject error CDATA #IMPLIED> > <!-- Projection. --> <!ELEMENT projection (couple*)> <!ELEMENT couple EMPTY> <!ATTLIST couple id1 CDATA #REQUIRED id2 CDATA #REQUIRED > <!-- Demande du calcul des projections d'un graphe dans un autre. --> <!ELEMENT qprojections (projectionconfig?)> <!ATTLIST qprojections env CDATA #REQUIRED <!-- id1: graphe projeté. --> id1 CDATA #REQUIRED <!-- id2: graphe dans lequel on projette. --> id2 CDATA #REQUIRED > <!ELEMENT projectionconfig EMPTY> <!ATTLIST projectionconfig <!-- Nombre max de projections trouvées. 0 pour ne pas limiter. --> maxsize CDATA #IMPLIED 0 <!-- Si true les projections (ensemble de couples) sont retournées, sinon, seul le nombre est retourné. --> memorize CDATA #IMPLIED "true" > <!ELEMENT projections (projection*)> <!ATTLIST projections <!-- Le nombre de projections trouvées. --> size CDATA #REQUIRED > <!-- Demande du calcul des applications d'une règle dans un graphe. --> <!ELEMENT qruleapplications (projectionconfig?)> <!ATTLIST qruleapplications env CDATA #REQUIRED <!-- idr: identificateur de la règle. --> idr CDATA #REQUIRED <!-- idg: identificateur du graphe. --> idg CDATA #REQUIRED > <!ELEMENT ruleapplications (projection*)> <!ATTLIST ruleapplications size CDATA #REQUIRED > <!-- Application d'une règle sur un graphe selon une projection. --> <!ELEMENT qruleapply (projection)> <!ATTLIST qruleapply env CDATA #REQUIRED idr CDATA #REQUIRED idg CDATA #REQUIRED > <!ELEMENT ruleapply EMPTY> <!ATTLIST ruleapply error CDATA #IMPLIED> > <!-- Fermeture d'un graphe par un ensemble de règles.--> <!-- Les éléments eoid fils de la balise doivent contenir les identificateurs des règles à appliquer. Si aucun eoid n'est précisé, toutes les règles de l'environnement sont utilisées. --> <!ELEMENT qrulesclosure (eoid*)> <!ATTLIST qrulesclosure env CDATA #REQUIRED <!-- Identificateur du graphe. L'application de l'opération modifie ce graphe. --> idg CDATA #REQUIRED <!-- Nombre maximum d'applications de règles. Par défaut 0, i.e. applications illimitées. --> maxa CDATA '0' > <!ELEMENT rulesclosure EMPTY> <!ATTLIST rulesclosure error CDATA #IMPLIED > <!-- Vérification de la validité d'un graphe par rapport à une contrainte. --> <!ELEMENT qconstraintsatisfaction> <!ATTLIST qconstraintsatisfaction env CDATA #REQUIRED <!-- idr: identificateur de la contrainte. --> idc CDATA #REQUIRED <!-- idg: identificateur du graphe. --> idg CDATA #REQUIRED > <!ELEMENT constraintsatisfaction> <!ATTLIST constraintsatisfaction <!-- result: "true" si le graphe vérifie la contrainte, "false" sinon. --> result CDATA #REQUIRED > <!-- Copie d'un objet de l'environnement (graphe ou règle). --> <!ELEMENT qcopyenvironmentobject EMPTY> <!ATTLIST qcopyenvironmentobject env CDATA #REQUIRED idsrc CDATA #REQUIRED iddst CDATA #REQUIRED > <!ELEMENT copyenvironmentobject EMPTY> <!ATTLIST copyenvironmentobject error CDATA #IMPLIED > <!-- Modification de la configuration d'une opération. --> <!ELEMENT qoperationconfig EMPTY> <!ATTLIST qoperationconfig env CDATA #REQUIRED <!-- ope: opération concernée, param: nom du paramètre à modifier, value: valeur. --> <!-- Les valeurs autorisées pour l'instant sont : - ope : iohandler param : graphobjectidentifiers value : true | false Si défini à true, l'opération de chargement de graphes conserve dans la propriété Property::IDENTIFIER l'identificateur lu dans le fichier. --> ope (iohandler) #REQUIRED param (graphobjectidentifiers) #REQUIRED value (true | false) #REQUIRED > <!ELEMENT operationconfig EMPTY> <!ATTLIST operationconfig error CDATA #IMPLIED > <!-- Ajout d'éléments dans un graphe. --> <!-- Ajout d'un sommet concept dans un graphe. --> <!ELEMENT qaddconcept> <!ATTLIST qaddconcept env CDATA #REQUIRED <!-- idg : identificateur du graphe. --> idg CDATA #REQUIRED <!-- idp : identificateur de l'InternalGraph dans lequel le sommet doit être créé, 0 par défaut. --> idp CDATA "0" <!-- idt : type de concept. Il s'agit de l'iSet du type à créer. --> idt CDATA #REQUIRED <!-- idm : marqueur individuel. Ne pas utiliser cet attribut pour créer un sommet concept générique. Si cet attribut est utilisé, il doit contenir l'iSet du marqueur. --> idm CDATA #IMPLIED > <!ELEMENT addconcept EMPTY> <!ATTLIST addconcept <!-- id : identificateur de l'objet créé dans le graphe. --> id CDATA #IMPLIED error CDATA #IMPLIED > <!-- Ajout d'un sommet relation, cf. qaddconcept. --> <!ELEMENT qaddrelation> <!ATTLIST qaddrelation env CDATA #REQUIRED idg CDATA #REQUIRED idp CDATA "0" idt CDATA #REQUIRED > <!ELEMENT addrelation EMPTY> <!ATTLIST addrelation id CDATA #IMPLIED error CDATA #IMPLIED > <!-- Ajout d'un emboîtement, cf. qaddconcept. --> <!ELEMENT qaddnesting> <!ATTLIST qaddnesting env CDATA #REQUIRED idg CDATA #REQUIRED idp CDATA #REQUIRED idt CDATA #REQUIRED > <!ELEMENT addnesting EMPTY> <!ATTLIST addnesting id CDATA #IMPLIED error CDATA #IMPLIED > <!-- Suppression d'un élément d'un graphe. --> <!ELEMENT qdelgraphobject> <!ATTLIST qdelgraphobject env CDATA #REQUIRED <!-- idg : identificateur du graphe. --> idg CDATA #REQUIRED <!-- id : identificateur de l'objet à détruire (iSet). --> id CDATA #REQUIRED > <!ATTLIST delgraphobject EMPTY> <!ATTLIST delgraphobject error CDATA #IMPLIED > <!-- Ajout d'une arête au graphe. --> <!ELEMENT qaddedge EMPTY> <!ATTLIST qaddedge env CDATA #REQUIRED <!-- idg : identificateur du graphe. --> idg CDATA #REQUIRED <!-- idr : identificateur du sommet relation dans le graphe (iSet). --> idr CDATA #REQUIRED <!-- idr : identificateur du sommet concept dans le graphe (iSet). --> idc CDATA #REQUIRED <!-- lab : étiquette de l'arête à créer. L'étiquette doit avoir une valeur inférieure ou égale à l'arité du type de relation du sommet relation. --> lab CDATA #REQUIRED > <!ELEMENT addedge EMPTY> <!ATTLIST addedge error CDATA #IMPLIED > <!-- Suppression d'une arête d'un graphe. --> <!ELEMENT qdeledge> <!ATTLIST qdeledge env CDATA #REQUIRED <!-- idg : identificateur du graphe. --> idg CDATA #REQUIRED <!-- idr : identificateur du sommet relation (iSet). --> idr CDATA #REQUIRED <!-- lab : étiquette de l'arête à supprimer. --> lab CDATA #REQUIRED > <!ATTLIST deledge EMPTY> <!ATTLIST deledge error CDATA #IMPLIED >