The components of the GCF
To understand the structure of the GCF, you have to realize, what in detail happens during a conversion from any type of document to another (means a conversion in general, not only from a graph exchange format to another !). Figure 1 shows the main components that are useful and how they work together.

Figure 1 : The structure of a conversion
As you can see, three main components are necessary. First the source document is parsed by a parser. After this, the parser gives the results to the component that's responsible for the conversion of the single structures of the source document. And finally an output component can be used to write the results into the target document.
The red marked area between the parser and the conversion component shall show, that another component can be useful there. This component could be used to format the incoming parser results before they are given to the conversion component. So it would be nothing more than an adapter for the parser interface and the interface of the conversion component.
Having understood figure 1, the first conclusion is that a preconfigured XML parser is pretty useful for the GCF. It could be used to parse the source document during a GXL2<...> conversion. For the other direction the user has to provide a parser himself (at least, if the source format is no XML vocabulary).
The second conclusion is that the conversion component has to be implemented by the user. The only thing the GCF can do to support him, is to provide an interface, which somehow reflects the GXL-DTD.
As this interface tends to be quite extensive it is useful to provide a component to make it's use easier. This component has to be placed in the red marked area in figure 1 and offers an interface to adapt the parser interface and the conversion component properly.
All these thoughts have lead to the current structure of the GCF. It consists of six components, which are shown in the table below.
| component of the GCF | component in figure 1 |
| AElfred Parser (XML parser) | parser |
| GXLDocumentHandler | parser |
|
GXLConnector/GXLConnector2 |
red marked area between parser and conversion component |
| abstract class hierarchy | conversion component |
| GXLOutputAPI | output component |
| GXLConverterAPI | none |
Table 1 : The components of the GCF
The first important thing in table 1 is that the parser in diagram 1 is realized by two components in the GCF. This comes due to the fact that the AElfred parser is an event-based one (for further explanations see below). Therefore a further component is necessary to handle the events raised by the parser. That's the responsibility of the GXLDocumentHandler.
Furthermore you can see, that the GXLConnector/GXLConnector2 is the adapter between the parser (GXLDocumentHandler in the GCF) and the conversion component (abstract class hierarchy in the GCF). The output component in figure 1 is realized by the GXLOutputAPI in the GCF. The GXLConverterAPI is the only component in the GCF that matches no component in figure 1. It is a component to make the GCF user friendly by providing an interface to easily initialize the components of the framework.
Now, that the common structure of the GCF should be clear, each component is shortly described.
The AElfred parser
As mentioned before, the AElfred parser is an event-based (SAX) XML parser. So it raises an event for each element it finds in the document. The event is handled by a separate class called the event handler. The GXLDocumentHandler is the event handler of the GCF.
The alternative to the event-based parser would be a DOM parser which generates a tree of the document structure. As GXL files, as well as files of other graph exchange formats, tend to contain pretty many elements, this variant is not recommended. It would potentially cause memory problems.
The AElfred parser furthermore was chosen because it's pretty small (about 220 kb) and therefore easy to handle and to adapt to the GCF.
The possible events that can be raised by the parser are shown in table 2.
| event name | parameters | |||
| startDocument | none | |||
| endDocument | none | |||
| doctypeDecl |
|
|||
| processingInstruction |
|
|||
| startElement | name : String | |||
| endElement | name : String | |||
| attribute |
|
|||
| charData |
|
Table 2 : Possible events generated by the AElfred parser
The events startDocument/endDocument are generated when the parser reaches the begin/end of the source document. The events doctypeDecl/processingInstruction is raised when the parser finds a doctype declaration/processing instruction. For further information on these XML constructs see http://www.xml.org. When the parser reaches a start tag / end tag of an element, the events startElement/endElement are raised. The name of the corresponding element is given in the parameter name. The event attribute is generated when the parser finds an attribute belonging to an element. It's name and value are given in the parameters name and value. As the DTD mechanism of XML offers the possibility to give attributes a default value, not every attribute has to be specified in an XML document. Therefore, the parameter isSpecified is set true, if an attribute is specified in the XML document, and false otherwise.
The GXLDocumentHandler
The GXLDocumentHandler 's task is to handle the events raised by the AElfred Parser. Therefore it offers a handler method for each event shown in table 2. Furthermore an instance of this class must be given to the parser. The events are handled in the way, that the GXLDocumentHandler calls a handler method in the GXLConnector. This action shall be explained with an example. If the AElfred parser parses a GXL document and finds an element of type node, it raises the event startElement by calling the method startElement (<elementType>) in the GXLDocumentHandler. After that the GXLDocumentHandler calls the method create (<elementType>) in the GXLConnector. Finally the GXLConnector has to handle this event. The next paragraph explains the GXLConnector in detail.
The GXLConnector/GXLConnector2
The GXLConnector is used to adapt the parser interface to the abstract class hierarchy. The abstract class hierarchy reflects the GXL graph model and offers methods to
a) create and close child elements
b) create attributes
for each GXL construct. These methods have to be implemented by the user. For further information on the abstract class hierarchy see the next paragraph and the UML class diagram.
What the GXLConnector does, is to call the right method in the abstract class hierarchy, according to a parser event. This is done by special handler methods. Table 3 shows the methods offered by the GXLConnector.
| method | parameters | |||
| create | name : String | |||
| close | name : String | |||
| setAttributeValue |
|
|||
| printData | data : String | |||
| createDoctypeDecl |
|
|||
| createProcessingInstruction |
|
Table 3 : The methods of the GXLConnector
If you compare table 2 to table 3, you can see that each parser event (except the startDocument and endDocument events) has a corresponding method in the GXLConnector. The methods create() and close () are called, when the events startElement or endElement are raised. The methods createDoctypeDecl() and createProcessingInstruction() belong to the events doctypeDecl and processingInstruction. And finally the methods setAttributeValue()/printData() are called due to the parser events attribute and charData.
How these methods work, shall be shown with a short example. If a graph has been created (due to the parser event startElement ("graph") ) and the parser finds a new element of type node, the method createNode() of the class GXLGraphAPIImpl has to be called. To do so, the GXLConnector must hold a kind of abstract syntax tree of the document in memory. This is done by a stack. To see how it works, table 4 exemplarily shows the content of the stack while processing a typical parser event stream.
| parser event | method call in the GXLConnector | stack content |
| startDocument | none | empty |
| startElement ("gxl") | create ("gxl") | GXLGXLAPIImpl |
| startElement ("graph") | create ("graph") | GXLGXLAPIImpl GXLGraphAPIImpl |
| attribute ("ID", "abc", true) | setAttributeValue ("ID", "abc") | GXLGXLAPIImpl GXLGraphAPIImpl |
| ... | ... |
GXLGXLAPIImpl GXLGraphAPIImpl ... |
| endElement ("graph") | close ("graph") | GXLGXLAPIImpl |
| endElement ("gxl") | close ("gxl") | empty |
| endDocument | none | empty |
Table 4 : Stack content during a typical parser event stream
In table 4 you can see, that first the startDocument event and then the startElement ("gxl") event is generated by the parser. This causes the method call create ("gxl") in the GXLConnector ( method call is provided by the GXLDocumentHandler) and an instance of the class GXLGXLAPIImpl is created. Then the GXLConnector saves the object in the stack.
After that the event startElement ("graph") is raised by the parser. Now the GXLConnector knows that the graph construct belongs to the recently created gxl and calls the Method createGraph() of the object in the stack. After that the new created instance of the class GXLGraphAPIImpl is saved in the stack, so that it now contains two objects - one for the created gxl and one for the graph.
Now the last object in the stack is an instance of the GXLGraphAPIImpl, so that the parser event attribute ("ID", "abc", true) triggers the method call setAttributeValue ("ID", "abc") in the recently created graph construct.The stack is not changed, because the graph construct is still the current node in the document structure.
After several possible other events, the event endElement ("graph") is generated. Due to this, the graph construct is closed and deleted from the stack. Now the current object in the document structure is gxl. So the only object in the stack is the created gxl object.
The same happens to the gxl object after the parser event endElement ("gxl"), so that the stack is empty after this event. After that the parser reaches the end of the document and so the document is completed.
Sometimes it can be useful for the user to know the current depth of the stack (e.g. the default implementation 1 uses this feature). Therefore the GXLConnector saves the current depth of the stack in the variable current_depth and writes every change of this variable to the GXLOutputAPI (through the method call setCurrentDepth (<depth>) ). The user can get the current depth by using the method call getCurrentDepth(). For further information see the paragraph about the GXLOutputAPI.
The GXLConnector2 is a subclass of the GXLConnector. Its only task is to gain efficiency by not using JAVA's reflection API. The constructor of the GXLConnector2 therefore doesn't need a implementation URL or a package name of the implementation of the abstract class hierarchy. The user has to change the paths for the method calls manually. For further information have a look at the source code section.
When I speak of the GXLConnector in further explainations, u can substitute it by the GXLConnector2 as well. If there's a difference between these two classes it will explicitely be mentioned.
The abstract class hierarchy
As mentioned above, the abstract class hierarchy is the conversion component (fig.1) and reflects the GXL graph model. Therefore the GCF can convert any type of program graphs supported by GXL. The abstract class hierarchy has to be implemented by the user. Figure 2 shows the UML class diagram.

Figure 2 : UML class diagram of the abstract class hierarchy
The central class is the GXLStandardAPI. It offers methods to set attributes ( setAttributeValue() ), to close the current construct ( close() ) and to return the created child elements ( getChildElements() ) and the values of all attributes ( getAttributes() ). Each class in figure 2 is directly or indirectly derived form the GXLStandardAPI. The blue marked classes represent the possible constructs of the GXL DTD. In general, you can see that (except the GXLStandardAPI) each class offers methods to create ( create<elementType>() ) and to close ( close<elementType>) ) the possible child elements (according to the GXL graph model. To do so, the classes GXLAttrAPIImpl and GXLUntypedStandardValueContainer implement the interface GXLStandardValueMethods, which offers methods to create (create<elementType>() ) and close (close<elementType() ) the values shown in the value part of the GXL graph model. This detour was necessary because in Java classes can only be derived from one class. It is important to know that the create<elementType>() methods return the created object to the calling class. Thus it is possible to manipulate these objects.
The GXLGXLAPIImpl offers some additional methods. The first one is the method createGXL() which is called by the GXLConnector when the parsing is started. Furthermore the class GXLGXLAPIImpl contains the methods createDoctypeDecl() and createProcessingInstruction() which have to be implemented by the user in order to handle doctype declarations and processing instructions.
To show the connections between the abstract class hierarchy and the GXL graph model, table 5 shows each class from figure 2 and its corresponding class.
| class/interface of the abstract class hierarchy | class in the GXL graph model |
|
GXLStandardAPI |
none |
|
GXLAttributedAPI |
attributedElement |
|
GXLTypedAndAttributedAPI |
typedElement |
|
GXLGraphContainerAPI |
graphElement |
|
GXLStandardValueMethods |
none |
|
GXLUntypedStandardValueContainerAPI |
composite |
|
GXLGXLAPIImpl |
gxl |
|
GXLGraphAPIImpl |
graph |
|
GXLNodeAPIImpl |
node |
|
GXLEdgeAPIImpl |
edge |
|
GXLRelAPIImpl |
relation |
|
GXLRelendAPIImpl |
relend |
|
GXLTypeAPIImpl |
type |
|
GXLAttrAPIImpl |
attribute |
|
GXL
LocatorAPIImpl |
locator |
|
GXLSetAPIImpl |
set |
|
GXLSeqAPIImpl |
seq |
|
GXLTupAPIImpl |
tup |
|
GXLBagAPIImpl |
bag |
Table 5 : The classes of the abstract class hierarchy and their corresponding classes in the GXL graph model
As you can see, the class GXLStandardAPI and the interface GXLStandardValueMethods are the only components in the abstract class hierarchy, that have no corresponding component in the GXL graph model. The relation between the GXLAttributedAPI and the attributedElement should be clear, as well as the relation between the GXLTypedAndAttributedAPI and the typedElement.
Some words have to be said about the GXLGraphContainerAPI. The name GXLGraphContainerAPI was chosen because the methods createGraph() and closeGraph() are defined in this class. As only the gxl element and graph elements can contain graphs, this class can be compared to the class graphElement in the GXL graph model. Due to the fact that the gxl element is not typed or attributed, the class GXLGXLAPIImpl could not derived from this class. Thus the methods createGraph() and closeGraph() had to be defined twice, once in the GXLGraphContainerAPI and once in the GXLGXLAPIImpl.
The relations between the remaining classes in table 5 should be clear.
With this structure of the abstract class hierarchy the GCF covers each type of program graph supported by GXL DTD. It can parse them and convert them into other formats. But it can furthermore output them as the result of a conversion from any other graph exchange format to GXL. To support the output of the resulting document, the GCF contains the GXLOutputAPI, which shall be explained now.
The GXLOutputAPI
The task of the GXLOutputAPI is to centralize some often needed output routines so that the user has a simple interface to write documents. The methods offered by the GXLOutputAPI are shown in table 6.
| method name | parameters |
|
createOutputFile |
outFile
: String |
|
closeOutputFile |
none |
|
write |
text :
String |
|
writeln |
text :
String |
|
writeln |
none |
|
setCurrentDepth |
depth : Integer |
|
getCurrentDepth |
keine |
Table 6 : The methods of the GXLOutputAPI and their parameters
The method createOutputFile() creates an output file with the in the parameter outFile given name. Is the parameter outFile null or createOutputFile() isn't called, the standard output is set to System.out (screen). The method closeOutputFile() closes the output file.
The method write() writes the in the parameter text given String to the output file. There are two versions of the method writeln(). The first one does the same as the write() method, but it furthermore appends a carriage return. The second one only appends a carriage return.
The methods setCurrentDepth() / getCurrentDepth() can be used to set/get the current depth of the stack in the GXLConnector. As mentioned before, this depth sometimes can be useful for the user. For example the default implementation 1, which outputs a gxl file due to the event stream coming from the GXLConnector, uses the current depth of the stack to format the document.
It is important to know, that each implementation of the abstract classes has its own GXLOutputAPI. This is necessary because the GXLOutputAPI can only manage one output file and different implementations commonly will use different output files.
The GXLConverterAPI
The GXLConverterAPI is an interface to make the GCF easier to handle for the user. It does not offer any functionality that is important for the GCF to work. It is a kind of manager and centralizes all functionality of the GCF in one class. Table 7 shows all methods of the GXLConverterAPI. All these methods are declared static.
| method name | parameters |
|
createGXL |
GXL:Object |
|
getResultObject |
none |
|
closeGXL |
none |
|
setImplementationURL |
location
: URL |
|
getImplementationURL |
none |
|
setPackageName |
name :
String |
|
getPackageName |
none |
|
createConnector |
which : Integer |
|
getConnector |
none |
|
closeConnector |
none |
|
createDocumentHandler |
none |
|
getDocumentHandler |
none |
|
closeDocumentHandler |
none |
|
parse |
fileToParse
: String |
Table 7 : The methods of the GXLConverterAPI and their parameters
The task of the method createGXL() is to save the user implementation of the class GXLGXLAPIImpl. This object is needed by the GXLConnector because it is the root of the syntax tree of the resulting document. The method getResultObject() has to be called after a conversion to get the structure tree of the resulting document. The method closeGXL() is called when the created GXLObject shall be closed. This method is not implemented yet but probably will be needed in future versions.
To work properly, the GXLConnector needs the URL, where the user's implementation of the abstract class hierarchy is located. Therefore the GXLConverterAPI offers the methods setImplementationURL() and getImplementationURL() so that the user can set/get the URL of his implementation.
The methods setPackageName() and getPackageName() work in a analogous way. An example shall make clear, how the package name and the implementation URL have to be set in detail. If you have placed the implementation of the abstract class hierarchy in the folder c:\GCF\Impl, you have to set the URL and the package name with the following instructions :
setImplementationURL ("file://c:/GCF");
setPackageName ("Impl");
The methods createConnector(), getConnector() and closeConnector() can be used for the management of the GXLConnector/GXLConnector2. It is important that the methods createGXL(), setImplementationURL() and setPackageName() are called before the GXLConnector is created. This is necessary, because the GXLConnector needs these parameters. This does not apply to the GXLConnector2. This class only needs the GXLObject for its constructor. Call the method createConnector() with the parameter CONNECTOR to create an instance of the GXLConnector and with the parameter CONNECTOR2 to create an instance of the GXLConnector2.
The methods createDocumentHandler(), getDocumentHandler() and closeDocumentHandler() work in a analogous way for the GXLDocumentHandler. It is important here that the GXLConnector must be created before the GXLDocumentHandler because an instance of the GXLConnector is needed by the GXLDocumentHandler to handle the incoming parser events.
The method parse() has to be called when all necessary variables are initialized and the parsing of the source document shall be started.
Now that all components of the GCF have been described, a short summary of their functionality shall be given in the next chapter (Functionality).