/*
* $Revision: 2565 $
*
* last checkin:
* $Author: gutwenger $
* $Date: 2012-07-07 17:14:54 +0200 (Sa, 07. Jul 2012) $
***************************************************************/
/** \file
* \brief Implementation of GML parser (class GmlParser)
* (used for parsing and reading GML files)
*
* \author Carsten Gutwenger
*
* \par License:
* This file is part of the Open Graph Drawing Framework (OGDF).
*
* \par
* Copyright (C)
* See README.txt in the root directory of the OGDF installation for details.
*
* \par
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* Version 2 or 3 as published by the Free Software Foundation;
* see the file LICENSE.txt included in the packaging of this file
* for details.
*
* \par
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* \par
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* \see http://www.gnu.org/copyleft/gpl.html
***************************************************************/
#include "GmlParser.h"
#include
#include
extern ofstream os;
namespace ogdf {
GmlParser::GmlParser(const char *fileName, bool doCheck)
{
ifstream is(fileName, ios::in); // open file
doInit(is,doCheck);
}
GmlParser::GmlParser(istream &is, bool doCheck)
{
doInit(is,doCheck);
}
void GmlParser::doInit(istream &is, bool doCheck)
{
m_objectTree = 0;
if (!is) {
setError("Cannot open file.");
return;
}
createObjectTree(is,doCheck);
int minId, maxId;
m_graphObject = getNodeIdRange(minId, maxId);
m_mapToNode.init(minId,maxId,0);
}
void GmlParser::createObjectTree(istream &is, bool doCheck)
{
initPredefinedKeys();
m_error = false;
m_is = &is;
m_doCheck = doCheck; // indicates more extensive checking
// initialize line buffer (note: GML specifies a maximal line length
// of 254 characters!)
m_rLineBuffer = new char[256];
if (m_rLineBuffer == 0) OGDF_THROW(InsufficientMemoryException);
*m_rLineBuffer = '\n';
m_lineBuffer = m_rLineBuffer+1;
m_pCurrent = m_pStore = m_lineBuffer;
m_cStore = 0; // forces getNextSymbol() to read first line
// create object tree
m_objectTree = parseList(gmlEOF,gmlListEnd);
delete[] m_rLineBuffer;
}
// we use predefined id constants for all relevant keys
// this allows us to use efficient switch() statemnts in read() methods
void GmlParser::initPredefinedKeys()
{
m_hashTable.fastInsert("id", idPredefKey);
m_hashTable.fastInsert("label", labelPredefKey);
m_hashTable.fastInsert("Creator", CreatorPredefKey);
m_hashTable.fastInsert("name", namePredefKey);
m_hashTable.fastInsert("graph", graphPredefKey);
m_hashTable.fastInsert("version", versionPredefKey);
m_hashTable.fastInsert("directed", directedPredefKey);
m_hashTable.fastInsert("node", nodePredefKey);
m_hashTable.fastInsert("edge", edgePredefKey);
m_hashTable.fastInsert("graphics", graphicsPredefKey);
m_hashTable.fastInsert("x", xPredefKey);
m_hashTable.fastInsert("y", yPredefKey);
m_hashTable.fastInsert("w", wPredefKey);
m_hashTable.fastInsert("h", hPredefKey);
m_hashTable.fastInsert("type", typePredefKey);
m_hashTable.fastInsert("width", widthPredefKey);
m_hashTable.fastInsert("source", sourcePredefKey);
m_hashTable.fastInsert("target", targetPredefKey);
m_hashTable.fastInsert("arrow", arrowPredefKey);
m_hashTable.fastInsert("Line", LinePredefKey);
m_hashTable.fastInsert("line", linePredefKey);
m_hashTable.fastInsert("point", pointPredefKey);
m_hashTable.fastInsert("generalization", generalizationPredefKey);
m_hashTable.fastInsert("subgraph", subGraphPredefKey);
m_hashTable.fastInsert("fill", fillPredefKey);
m_hashTable.fastInsert("cluster", clusterPredefKey);
m_hashTable.fastInsert("rootcluster", rootClusterPredefKey);
m_hashTable.fastInsert("vertex", vertexPredefKey);
m_hashTable.fastInsert("color", colorPredefKey);
m_hashTable.fastInsert("height", heightPredefKey);
m_hashTable.fastInsert("stipple", stipplePredefKey); //linestyle
m_hashTable.fastInsert("pattern", patternPredefKey); //brush pattern
m_hashTable.fastInsert("lineWidth", lineWidthPredefKey);//line width
m_hashTable.fastInsert("template", templatePredefKey);//line width
m_hashTable.fastInsert("weight", edgeWeightPredefKey);
// further keys get id's starting with NEXTPREDEFKEY
m_num = NEXTPREDEFKEY;
}
GmlObject *GmlParser::parseList(GmlObjectType closingKey,
GmlObjectType /* errorKey */)
{
GmlObject *firstSon = 0;
GmlObject **pPrev = &firstSon;
for( ; ; ) {
GmlObjectType symbol = getNextSymbol();
if (symbol == closingKey || symbol == gmlError)
return firstSon;
if (symbol != gmlKey) {
setError("key expected");
return firstSon;
}
GmlKey key = m_keySymbol;
symbol = getNextSymbol();
GmlObject *object = 0;
switch (symbol) {
case gmlIntValue:
object = OGDF_NEW GmlObject(key,m_intSymbol);
break;
case gmlDoubleValue:
object = OGDF_NEW GmlObject(key,m_doubleSymbol);
break;
case gmlStringValue: {
size_t len = strlen(m_stringSymbol)+1;
char *pChar = new char[len];
if (pChar == 0) OGDF_THROW(InsufficientMemoryException);
ogdf::strcpy(pChar,len,m_stringSymbol);
object = OGDF_NEW GmlObject(key,pChar); }
break;
case gmlListBegin:
object = OGDF_NEW GmlObject(key);
object->m_pFirstSon = parseList(gmlListEnd,gmlEOF);
break;
case gmlListEnd:
setError("unexpected end of list");
return firstSon;
case gmlKey:
setError("unexpected key");
return firstSon;
case gmlEOF:
setError("missing value");
return firstSon;
case gmlError:
return firstSon;
OGDF_NODEFAULT // one of the cases above has to occur
}
*pPrev = object;
pPrev = &object->m_pBrother;
}
return firstSon;
}
void GmlParser::destroyObjectList(GmlObject *object)
{
GmlObject *nextObject;
for(; object; object = nextObject) {
nextObject = object->m_pBrother;
if (object->m_valueType == gmlStringValue)
delete[] const_cast(object->m_stringValue);
else if (object->m_valueType == gmlListBegin)
destroyObjectList(object->m_pFirstSon);
delete object;
}
}
GmlParser::~GmlParser()
{
// we have to delete all objects and allocated char arrays in string values
destroyObjectList(m_objectTree);
}
bool GmlParser::getLine()
{
do {
if (m_is->eof()) return false;
(*m_is) >> std::ws; // skip whitespace like spaces for indentation
m_is->getline(m_lineBuffer,255);
if (m_is->fail())
return false;
for(m_pCurrent = m_lineBuffer;
*m_pCurrent && isspace(*m_pCurrent); ++m_pCurrent) ;
} while (*m_pCurrent == '#' || *m_pCurrent == 0);
return true;
}
GmlObjectType GmlParser::getNextSymbol()
{
*m_pStore = m_cStore;
// eat whitespace
for(; *m_pCurrent && isspace(*m_pCurrent); ++m_pCurrent) ;
// get new line if required
if (*m_pCurrent == 0) {
if (!getLine()) return gmlEOF;
}
// identify start of current symbol
char *pStart = m_pCurrent;
// we currently do not support strings with line breaks!
if (*pStart == '\"')
{ // string
m_stringSymbol = ++m_pCurrent;
char *pWrite = m_pCurrent;
while(*m_pCurrent != 0 && *m_pCurrent != '\"')
{
if (*m_pCurrent == '\\')
{
// note: this block is repeated below
switch(*(m_pCurrent+1)) {
case 0:
*m_pCurrent = 0;
break;
case '\\':
*pWrite++ = '\\';
m_pCurrent += 2;
break;
case '\"':
*pWrite++ = '\"';
m_pCurrent += 2;
break;
default:
// just copy the escape sequence as is
*pWrite++ = *m_pCurrent++;
*pWrite++ = *m_pCurrent++;
}
} else
*pWrite++ = *m_pCurrent++;
}
if (*m_pCurrent == 0)
{
*pWrite = 0;
m_longString = (pStart+1);
while(getLine())
{
m_pCurrent = pWrite = m_lineBuffer;
while(*m_pCurrent != 0 && *m_pCurrent != '\"')
{
if (*m_pCurrent == '\\')
{
// (block repeated from above)
switch(*(m_pCurrent+1)) {
case 0:
*m_pCurrent = 0;
break;
case '\\':
*pWrite++ = '\\';
m_pCurrent += 2;
break;
case '\"':
*pWrite++ = '\"';
m_pCurrent += 2;
break;
default:
// just copy the escape sequence as is
*pWrite++ = *m_pCurrent++;
*pWrite++ = *m_pCurrent++;
}
} else
*pWrite++ = *m_pCurrent++;
}
if (*m_pCurrent == 0) {
*pWrite = 0;
m_longString += m_lineBuffer;
} else {
m_cStore = *(m_pStore = m_pCurrent);
++m_pCurrent;
*pWrite = 0;
m_longString += m_lineBuffer;
break;
}
}
m_stringSymbol = m_longString.cstr();
} else {
m_cStore = *(m_pStore = m_pCurrent);
++m_pCurrent;
*pWrite = 0;
}
return gmlStringValue;
}
// identify end of current symbol
while(*m_pCurrent != 0 && !isspace(*m_pCurrent)) ++m_pCurrent;
m_cStore = *(m_pStore = m_pCurrent);
*m_pCurrent = 0;
if(isalpha(*pStart)) { // key
// check if really a correct key (error if not)
if (m_doCheck) {
for (char *p = pStart+1; *p; ++p)
if (!(isalpha(*p) || isdigit(*p))) {
setError("malformed key");
return gmlError;
}
}
m_keySymbol = hashString(pStart);
return gmlKey;
} else if (*pStart == '[') {
return gmlListBegin;
} else if (*pStart == ']') {
return gmlListEnd;
} else if (*pStart == '-' || isdigit(*pStart)) { // int or double
char *p = pStart+1;
while(isdigit(*p)) ++p;
if (*p == '.') { // double
// check to be done
sscanf(pStart,"%lf",&m_doubleSymbol);
return gmlDoubleValue;
} else { // int
if (*p != 0) {
setError("malformed number");
return gmlError;
}
sscanf(pStart,"%d",&m_intSymbol);
return gmlIntValue;
}
}
setError("unknown symbol");
return gmlError;
}
GmlKey GmlParser::hashString(const String &str)
{
GmlKey key = m_hashTable.insertByNeed(str,-1);
if(key->info() == -1) key->info() = m_num++;
return key;
}
GmlObject *GmlParser::getNodeIdRange(int &minId,int &maxId)
{
minId = maxId = 0;
GmlObject *graphObject = m_objectTree;
for(; graphObject; graphObject = graphObject->m_pBrother)
if (id(graphObject) == graphPredefKey) break;
if (!graphObject || graphObject->m_valueType != gmlListBegin) return 0;
bool first = true;
GmlObject *son = graphObject->m_pFirstSon;
for(; son; son = son->m_pBrother) {
if (id(son) == nodePredefKey && son->m_valueType == gmlListBegin) {
GmlObject *nodeSon = son->m_pFirstSon;
for(; nodeSon; nodeSon = nodeSon->m_pBrother) {
if (id(nodeSon) == idPredefKey ||
nodeSon->m_valueType == gmlIntValue)
{
int nodeSonId = nodeSon->m_intValue;
if (first) {
minId = maxId = nodeSonId;
first = false;
} else {
if (nodeSonId < minId) minId = nodeSonId;
if (nodeSonId > maxId) maxId = nodeSonId;
}
}
}
}
}
return graphObject;
}
bool GmlParser::read(Graph &G)
{
G.clear();
int minId = m_mapToNode.low();
int maxId = m_mapToNode.high();
int notDefined = minId-1; //indicates not defined id key
GmlObject *son = m_graphObject->m_pFirstSon;
for(; son; son = son->m_pBrother)
{
switch(id(son))
{
case nodePredefKey: {
if (son->m_valueType != gmlListBegin) break;
// set attributes to default values
int vId = notDefined;
// read all relevant attributes
GmlObject *nodeSon = son->m_pFirstSon;
for(; nodeSon; nodeSon = nodeSon->m_pBrother) {
if (id(nodeSon) == idPredefKey &&
nodeSon->m_valueType == gmlIntValue)
{
vId = nodeSon->m_intValue;
}
}
// check if everything required is defined correctly
if (vId == notDefined) {
setError("node id not defined");
return false;
}
// create new node if necessary
if (m_mapToNode[vId] == 0) m_mapToNode[vId] = G.newNode(); }
break;
case edgePredefKey: {
if (son->m_valueType != gmlListBegin) break;
// set attributes to default values
int sourceId = notDefined, targetId = notDefined;
// read all relevant attributes
GmlObject *edgeSon = son->m_pFirstSon;
for(; edgeSon; edgeSon = edgeSon->m_pBrother) {
switch(id(edgeSon)) {
case sourcePredefKey:
if (edgeSon->m_valueType != gmlIntValue) break;
sourceId = edgeSon->m_intValue;
break;
case targetPredefKey:
if (edgeSon->m_valueType != gmlIntValue) break;
targetId = edgeSon->m_intValue;
break;
}
}
// check if everything required is defined correctly
if (sourceId == notDefined || targetId == notDefined) {
setError("source or target id not defined");
return false;
} else if (sourceId < minId || maxId < sourceId ||
targetId < minId || maxId < targetId) {
setError("source or target id out of range");
return false;
}
// create adjacent nodes if necessary and new edge
if (m_mapToNode[sourceId] == 0) m_mapToNode[sourceId] = G.newNode();
if (m_mapToNode[targetId] == 0) m_mapToNode[targetId] = G.newNode();
G.newEdge(m_mapToNode[sourceId],m_mapToNode[targetId]);
}//case edge
break;
}//switch
}//for sons
return true;
}
bool GmlParser::read(Graph &G, GraphAttributes &AG)
{
OGDF_ASSERT(&G == &(AG.constGraph()))
G.clear();
int minId = m_mapToNode.low();
int maxId = m_mapToNode.high();
int notDefined = minId-1; //indicates not defined id key
DPolyline bends;
GmlObject *son = m_graphObject->m_pFirstSon;
for(; son; son = son->m_pBrother) {
switch(id(son)) {
case nodePredefKey: {
if (son->m_valueType != gmlListBegin) break;
// set attributes to default values
int vId = notDefined;
double x = 0, y = 0, w = 0, h = 0;
String label;
String templ;
String fill; // the fill color attribute
String line; // the line color attribute
String shape; //the shape type
double lineWidth = 1.0; //node line width
int pattern = 1; //node brush pattern
int stipple = 1; //line style pattern
// read all relevant attributes
GmlObject *nodeSon = son->m_pFirstSon;
for(; nodeSon; nodeSon = nodeSon->m_pBrother) {
switch(id(nodeSon)) {
case idPredefKey:
if(nodeSon->m_valueType != gmlIntValue) break;
vId = nodeSon->m_intValue;
break;
case graphicsPredefKey: {
if (nodeSon->m_valueType != gmlListBegin) break;
GmlObject *graphicsObject = nodeSon->m_pFirstSon;
for(; graphicsObject;
graphicsObject = graphicsObject->m_pBrother)
{
switch(id(graphicsObject)) {
case xPredefKey:
if(graphicsObject->m_valueType != gmlDoubleValue) break;
x = graphicsObject->m_doubleValue;
break;
case yPredefKey:
if(graphicsObject->m_valueType != gmlDoubleValue) break;
y = graphicsObject->m_doubleValue;
break;
case wPredefKey:
if(graphicsObject->m_valueType != gmlDoubleValue) break;
w = graphicsObject->m_doubleValue;
break;
case hPredefKey:
if(graphicsObject->m_valueType != gmlDoubleValue) break;
h = graphicsObject->m_doubleValue;
break;
case fillPredefKey:
if(graphicsObject->m_valueType != gmlStringValue) break;
fill = graphicsObject->m_stringValue;
break;
case linePredefKey:
if(graphicsObject->m_valueType != gmlStringValue) break;
line = graphicsObject->m_stringValue;
break;
case lineWidthPredefKey:
if(graphicsObject->m_valueType != gmlDoubleValue) break;
lineWidth = graphicsObject->m_doubleValue;
break;
case typePredefKey:
if(graphicsObject->m_valueType != gmlStringValue) break;
shape = graphicsObject->m_stringValue;
break;
case patternPredefKey: //fill style
if(graphicsObject->m_valueType != gmlIntValue) break;
pattern = graphicsObject->m_intValue;
case stipplePredefKey: //line style
if(graphicsObject->m_valueType != gmlIntValue) break;
stipple = graphicsObject->m_intValue;
}
}
break; }
case templatePredefKey:
if (nodeSon->m_valueType != gmlStringValue) break;
templ = nodeSon->m_stringValue;
break;
case labelPredefKey:
if (nodeSon->m_valueType != gmlStringValue) break;
label = nodeSon->m_stringValue;
break;
}
}
// check if everything required is defined correctly
if (vId == notDefined) {
setError("node id not defined");
return false;
}
// create new node if necessary and assign attributes
if (m_mapToNode[vId] == 0) m_mapToNode[vId] = G.newNode();
if (AG.attributes() & GraphAttributes::nodeGraphics)
{
AG.x(m_mapToNode[vId]) = x;
AG.y(m_mapToNode[vId]) = y;
AG.width (m_mapToNode[vId]) = w;
AG.height(m_mapToNode[vId]) = h;
if (shape == "oval")
AG.shapeNode(m_mapToNode[vId]) = GraphAttributes::oval;
else AG.shapeNode(m_mapToNode[vId]) = GraphAttributes::rectangle;
}
if ( (AG.attributes() & GraphAttributes::nodeColor) &&
(AG.attributes() & GraphAttributes::nodeGraphics) )
{
AG.colorNode(m_mapToNode[vId]) = fill;
AG.nodeLine(m_mapToNode[vId]) = line;
}
if (AG.attributes() & GraphAttributes::nodeLabel)
AG.labelNode(m_mapToNode[vId]) = label;
if (AG.attributes() & GraphAttributes::nodeTemplate)
AG.templateNode(m_mapToNode[vId]) = templ;
if (AG.attributes() & GraphAttributes::nodeId)
AG.idNode(m_mapToNode[vId]) = vId;
if (AG.attributes() & GraphAttributes::nodeStyle)
{
AG.nodePattern(m_mapToNode[vId]) =
GraphAttributes::intToPattern(pattern);
AG.styleNode(m_mapToNode[vId]) =
GraphAttributes::intToStyle(stipple);
AG.lineWidthNode(m_mapToNode[vId]) =
lineWidth;
}
}//node
//Todo: line style set stipple value
break;
case edgePredefKey: {
String arrow; // the arrow type attribute
String fill; //the color fill attribute
int stipple = 1; //the line style
double lineWidth = 1.0;
double edgeWeight = 1.0;
int subGraph = 0; //edgeSubGraph attribute
String label; // label attribute
if (son->m_valueType != gmlListBegin) break;
// set attributes to default values
int sourceId = notDefined, targetId = notDefined;
Graph::EdgeType umlType = Graph::association;
// read all relevant attributes
GmlObject *edgeSon = son->m_pFirstSon;
for(; edgeSon; edgeSon = edgeSon->m_pBrother) {
switch(id(edgeSon)) {
case sourcePredefKey:
if (edgeSon->m_valueType != gmlIntValue) break;
sourceId = edgeSon->m_intValue;
break;
case targetPredefKey:
if (edgeSon->m_valueType != gmlIntValue) break;
targetId = edgeSon->m_intValue;
break;
case subGraphPredefKey:
if (edgeSon->m_valueType != gmlIntValue) break;
subGraph = edgeSon->m_intValue;
break;
case labelPredefKey:
if (edgeSon->m_valueType != gmlStringValue) break;
label = edgeSon->m_stringValue;
break;
case graphicsPredefKey: {
if (edgeSon->m_valueType != gmlListBegin) break;
GmlObject *graphicsObject = edgeSon->m_pFirstSon;
for(; graphicsObject;
graphicsObject = graphicsObject->m_pBrother)
{
if(id(graphicsObject) == LinePredefKey &&
graphicsObject->m_valueType == gmlListBegin)
{
readLineAttribute(graphicsObject->m_pFirstSon,bends);
}
if(id(graphicsObject) == arrowPredefKey &&
graphicsObject->m_valueType == gmlStringValue)
arrow = graphicsObject->m_stringValue;
if(id(graphicsObject) == fillPredefKey &&
graphicsObject->m_valueType == gmlStringValue)
fill = graphicsObject->m_stringValue;
if (id(graphicsObject) == stipplePredefKey && //line style
graphicsObject->m_valueType == gmlIntValue)
stipple = graphicsObject->m_intValue;
if (id(graphicsObject) == lineWidthPredefKey && //line width
graphicsObject->m_valueType == gmlDoubleValue)
lineWidth = graphicsObject->m_doubleValue;
if (id(graphicsObject) == edgeWeightPredefKey &&
graphicsObject->m_valueType == gmlDoubleValue)
edgeWeight = graphicsObject->m_doubleValue;
}//for graphics
}
case generalizationPredefKey:
if (edgeSon->m_valueType != gmlIntValue) break;
umlType = (edgeSon->m_intValue == 0) ?
Graph::association : Graph::generalization;
break;
}
}
// check if everything required is defined correctly
if (sourceId == notDefined || targetId == notDefined) {
setError("source or target id not defined");
return false;
} else if (sourceId < minId || maxId < sourceId ||
targetId < minId || maxId < targetId) {
setError("source or target id out of range");
return false;
}
// create adjacent nodes if necessary and new edge
if (m_mapToNode[sourceId] == 0) m_mapToNode[sourceId] = G.newNode();
if (m_mapToNode[targetId] == 0) m_mapToNode[targetId] = G.newNode();
edge e = G.newEdge(m_mapToNode[sourceId],m_mapToNode[targetId]);
if (AG.attributes() & GraphAttributes::edgeGraphics)
AG.bends(e).conc(bends);
if (AG.attributes() & GraphAttributes::edgeType)
AG.type(e) = umlType;
if(AG.attributes() & GraphAttributes::edgeSubGraph)
AG.subGraphBits(e) = subGraph;
if (AG.attributes() & GraphAttributes::edgeLabel)
AG.labelEdge(e) = label;
if (AG.attributes() & GraphAttributes::edgeArrow) {
if (arrow == "none")
AG.arrowEdge(e) = GraphAttributes::none;
else if (arrow == "last")
AG.arrowEdge(e) = GraphAttributes::last;
else if (arrow == "first")
AG.arrowEdge(e) = GraphAttributes::first;
else if (arrow == "both")
AG.arrowEdge(e) = GraphAttributes::both;
else
AG.arrowEdge(e) = GraphAttributes::undefined;
}
if (AG.attributes() & GraphAttributes::edgeColor)
AG.colorEdge(e) = fill;
if (AG.attributes() & GraphAttributes::edgeStyle)
{
AG.styleEdge(e) = AG.intToStyle(stipple);
AG.edgeWidth(e) = lineWidth;
}
if (AG.attributes() & GraphAttributes::edgeDoubleWeight)
AG.doubleWeight(e) = edgeWeight;
break; }
case directedPredefKey: {
if(son->m_valueType != gmlIntValue) break;
AG.directed(son->m_intValue > 0);
break; }
}
}
return true;
}//read
//to be called AFTER calling read(G, AG)
bool GmlParser::readAttributedCluster(
Graph &/*G*/,
ClusterGraph& CG,
ClusterGraphAttributes& ACG)
{
// OGDF_ASSERT(&CG.getGraph() == &G)
//now we need the cluster object
GmlObject *rootObject = m_objectTree;
for(; rootObject; rootObject = rootObject->m_pBrother)
if (id(rootObject) == rootClusterPredefKey) break;
if(rootObject == 0)
return true;
if (id(rootObject) != rootClusterPredefKey)
{
setError("missing rootcluster key");
return false;
}
if (rootObject->m_valueType != gmlListBegin) return false;
attributedClusterRead(rootObject, CG, ACG);
return true;
}//readAttributedCluster
//the clustergraph has to be initialized on G!!,
//no clusters other then root cluster may exist, which holds all nodes
bool GmlParser::readCluster(Graph &/*G*/, ClusterGraph& CG)
{
// OGDF_ASSERT(&CG.getGraph() == &G)
//now we need the cluster object
GmlObject *rootObject = m_objectTree;
for(; rootObject; rootObject = rootObject->m_pBrother)
if (id(rootObject) == rootClusterPredefKey) break;
//we have to check if the file does really contain clusters
//otherwise, rootcluster will suffice
if (rootObject == 0) return true;
if (id(rootObject) != rootClusterPredefKey)
{
setError("missing rootcluster key");
return false;
}
if (rootObject->m_valueType != gmlListBegin) return false;
clusterRead(rootObject, CG);
return true;
}//read clustergraph
//read all cluster tree information
bool GmlParser::clusterRead(
GmlObject* rootCluster,
ClusterGraph& CG)
{
//the root cluster is only allowed to hold child clusters and
//nodes in a list
if (rootCluster->m_valueType != gmlListBegin) return false;
// read all clusters and nodes
GmlObject *rootClusterSon = rootCluster->m_pFirstSon;
for(; rootClusterSon; rootClusterSon = rootClusterSon->m_pBrother)
{
switch(id(rootClusterSon))
{
case clusterPredefKey:
{
//we could delete this, but we aviod the call
if (rootClusterSon->m_valueType != gmlListBegin) return false;
// set attributes to default values
//we currently do not set any values
cluster c = CG.newCluster(CG.rootCluster());
//recursively read cluster
recursiveClusterRead(rootClusterSon, CG, c);
} //case cluster
break;
case vertexPredefKey: //direct root vertices
{
if (rootClusterSon->m_valueType != gmlStringValue) return false;
String vIDString = rootClusterSon->m_stringValue;
//we only allow a vertex id as string identification
if ((vIDString[0] != 'v') &&
(!isdigit(vIDString[0])))return false; //do not allow labels
//if old style entry "v"i
if (!isdigit(vIDString[0])) //should check prefix?
vIDString[0] = '0'; //leading zero to allow conversion
int vID = atoi(vIDString.cstr());
OGDF_ASSERT(m_mapToNode[vID] != 0)
//we assume that no node is already assigned ! Changed:
//all new nodes are assigned to root
//CG.reassignNode(mapToNode[vID], CG.rootCluster());
//it seems that this may be unnessecary, TODO check
CG.reassignNode(m_mapToNode[vID], CG.rootCluster());
//char* vIDChar = new char[vIDString.length()+1];
//for (int ind = 1; ind < vIDString.length(); ind++)
// vIDChar
}//case vertex
}//switch
}//for all rootcluster sons
return true;
}//clusterread
//the same for attributed graphs
//read all cluster tree information
//make changes to this as well as the recursive function
bool GmlParser::attributedClusterRead(
GmlObject* rootCluster,
ClusterGraph& CG,
ClusterGraphAttributes& ACG)
{
//the root cluster is only allowed to hold child clusters and
//nodes in a list
if (rootCluster->m_valueType != gmlListBegin) return false;
// read all clusters and nodes
GmlObject *rootClusterSon = rootCluster->m_pFirstSon;
for(; rootClusterSon; rootClusterSon = rootClusterSon->m_pBrother)
{
switch(id(rootClusterSon))
{
case clusterPredefKey:
{
//we could delete this, but we avoid the call
if (rootClusterSon->m_valueType != gmlListBegin) return false;
// set attributes to default values
//we currently do not set any values
cluster c = CG.newCluster(CG.rootCluster());
//recursively read cluster
recursiveAttributedClusterRead(rootClusterSon, CG, ACG, c);
} //case cluster
break;
case vertexPredefKey: //direct root vertices
{
if (rootClusterSon->m_valueType != gmlStringValue) return false;
String vIDString = rootClusterSon->m_stringValue;
//we only allow a vertex id as string identification
if ((vIDString[0] != 'v') &&
(!isdigit(vIDString[0])))return false; //do not allow labels
//if old style entry "v"i
if (!isdigit(vIDString[0])) //should check prefix?
vIDString[0] = '0'; //leading zero to allow conversion
int vID = atoi(vIDString.cstr());
OGDF_ASSERT(m_mapToNode[vID] != 0)
//we assume that no node is already assigned
//CG.reassignNode(mapToNode[vID], CG.rootCluster());
//changed: all nodes are already assigned to root
//this code seems to be obsolete, todo: check
CG.reassignNode(m_mapToNode[vID], CG.rootCluster());
//char* vIDChar = new char[vIDString.length()+1];
//for (int ind = 1; ind < vIDString.length(); ind++)
// vIDChar
}//case vertex
}//switch
}//for all rootcluster sons
return true;
}//attributedclusterread
bool GmlParser::readClusterAttributes(
GmlObject* cGraphics,
cluster c,
ClusterGraphAttributes& ACG)
{
String label;
String fill; // the fill color attribute
String line; // the line color attribute
double lineWidth = 1.0; //node line width
int pattern = 1; //node brush pattern
int stipple = 1; //line style pattern
// read all relevant attributes
GmlObject *graphicsObject = cGraphics->m_pFirstSon;
for(; graphicsObject; graphicsObject = graphicsObject->m_pBrother)
{
switch(id(graphicsObject))
{
case xPredefKey:
if(graphicsObject->m_valueType != gmlDoubleValue) return false;
ACG.clusterXPos(c) = graphicsObject->m_doubleValue;
break;
case yPredefKey:
if(graphicsObject->m_valueType != gmlDoubleValue) return false;
ACG.clusterYPos(c) = graphicsObject->m_doubleValue;
break;
case widthPredefKey:
if(graphicsObject->m_valueType != gmlDoubleValue) return false;
ACG.clusterWidth(c) = graphicsObject->m_doubleValue;
break;
case heightPredefKey:
if(graphicsObject->m_valueType != gmlDoubleValue) return false;
ACG.clusterHeight(c) = graphicsObject->m_doubleValue;
break;
case fillPredefKey:
if(graphicsObject->m_valueType != gmlStringValue) return false;
ACG.clusterFillColor(c) = graphicsObject->m_stringValue;
break;
case patternPredefKey:
if(graphicsObject->m_valueType != gmlIntValue) return false;
pattern = graphicsObject->m_intValue;
break;
//line style
case colorPredefKey: // line color
if(graphicsObject->m_valueType != gmlStringValue) return false;
ACG.clusterColor(c) = graphicsObject->m_stringValue;
break;
case stipplePredefKey:
if(graphicsObject->m_valueType != gmlIntValue) return false;
stipple = graphicsObject->m_intValue;
break;
case lineWidthPredefKey:
if(graphicsObject->m_valueType != gmlDoubleValue) return false;
lineWidth =
graphicsObject->m_doubleValue;
break;
//TODO: backgroundcolor
//case stylePredefKey:
//case boderwidthPredefKey:
}//switch
}//for
//Hier eigentlich erst abfragen, ob clusterattributes setzbar in ACG,
//dann setzen
ACG.clusterLineStyle(c) = GraphAttributes::intToStyle(stipple); //defaulting 1
ACG.clusterLineWidth(c) = lineWidth;
ACG.clusterFillPattern(c) = GraphAttributes::intToPattern(pattern);
return true;
}//readclusterattributes
//recursively read cluster subtree information
bool GmlParser::recursiveClusterRead(GmlObject* clusterObject,
ClusterGraph& CG,
cluster c)
{
//for direct root cluster sons, this is checked twice...
if (clusterObject->m_valueType != gmlListBegin) return false;
GmlObject *clusterSon = clusterObject->m_pFirstSon;
for(; clusterSon; clusterSon = clusterSon->m_pBrother)
{
//we dont read the attributes, therefore look only for
//id and sons
switch(id(clusterSon))
{
case clusterPredefKey:
{
if (clusterSon->m_valueType != gmlListBegin) return false;
cluster cson = CG.newCluster(c);
//recursively read child cluster
recursiveClusterRead(clusterSon, CG, cson);
}
break;
case vertexPredefKey: //direct cluster vertex entries
{
if (clusterSon->m_valueType != gmlStringValue) return false;
String vIDString = clusterSon->m_stringValue;
//if old style entry "v"i
if ((vIDString[0] != 'v') &&
(!isdigit(vIDString[0])))return false; //do not allow labels
//if old style entry "v"i
if (!isdigit(vIDString[0])) //should check prefix?
vIDString[0] = '0'; //leading zero to allow conversion
int vID = atoi(vIDString.cstr());
OGDF_ASSERT(m_mapToNode[vID] != 0)
//we assume that no node is already assigned
//CG.reassignNode(mapToNode[vID], c);
//changed: all nodes are already assigned to root
CG.reassignNode(m_mapToNode[vID], c);
//char* vIDChar = new char[vIDString.length()+1];
//for (int ind = 1; ind < vIDString.length(); ind++)
// vIDChar
}//case vertex
}//switch
}//for clustersons
return true;
}//recursiveclusterread
//recursively read cluster subtree information
bool GmlParser::recursiveAttributedClusterRead(GmlObject* clusterObject,
ClusterGraph& CG,
ClusterGraphAttributes& ACG,
cluster c)
{
//for direct root cluster sons, this is checked twice...
if (clusterObject->m_valueType != gmlListBegin) return false;
GmlObject *clusterSon = clusterObject->m_pFirstSon;
for(; clusterSon; clusterSon = clusterSon->m_pBrother)
{
//we dont read the attributes, therefore look only for
//id and sons
switch(id(clusterSon))
{
case clusterPredefKey:
{
if (clusterSon->m_valueType != gmlListBegin) return false;
cluster cson = CG.newCluster(c);
//recursively read child cluster
recursiveAttributedClusterRead(clusterSon, CG, ACG, cson);
}
break;
case labelPredefKey:
{
if (clusterSon->m_valueType != gmlStringValue) return false;
ACG.clusterLabel(c) = clusterSon->m_stringValue;
}
break;
case templatePredefKey:
{
if (clusterSon->m_valueType != gmlStringValue) return false;
ACG.templateCluster(c) = clusterSon->m_stringValue;
break;
}
case graphicsPredefKey: //read the info for cluster c
{
if (clusterSon->m_valueType != gmlListBegin) return false;
readClusterAttributes(clusterSon, c , ACG);
}//graphics
break;
case vertexPredefKey: //direct cluster vertex entries
{
if (clusterSon->m_valueType != gmlStringValue) return false;
String vIDString = clusterSon->m_stringValue;
if ((vIDString[0] != 'v') &&
(!isdigit(vIDString[0])))return false; //do not allow labels
//if old style entry "v"i
if (!isdigit(vIDString[0])) //should check prefix?
vIDString[0] = '0'; //leading zero to allow conversion
int vID = atoi(vIDString.cstr());
OGDF_ASSERT(m_mapToNode[vID] != 0)
//we assume that no node is already assigned
//changed: all nodes are already assigned to root
CG.reassignNode(m_mapToNode[vID], c);
}//case vertex
}//switch
}//for clustersons
return true;
}//recursiveAttributedClusterRead
void GmlParser::readLineAttribute(GmlObject *object, DPolyline &dpl)
{
dpl.clear();
for(; object; object = object->m_pBrother) {
if (id(object) == pointPredefKey &&
object->m_valueType == gmlListBegin)
{
DPoint dp;
GmlObject *pointObject = object->m_pFirstSon;
for (; pointObject; pointObject = pointObject->m_pBrother) {
if (pointObject->m_valueType != gmlDoubleValue) continue;
if (id(pointObject) == xPredefKey)
dp.m_x = pointObject->m_doubleValue;
else if (id(pointObject) == yPredefKey)
dp.m_y = pointObject->m_doubleValue;
}
dpl.pushBack(dp);
}
}
}
void GmlParser::setError(const char *errorString)
{
m_error = true;
m_errorString = errorString;
}
void GmlParser::indent(ostream &os, int d)
{
for(int i = 1; i <= d; ++i)
os << " ";
}
void GmlParser::output(ostream &os, GmlObject *object, int d)
{
for(; object; object = object->m_pBrother) {
indent(os,d); os << object->m_key->key();
switch(object->m_valueType) {
case gmlIntValue:
os << " " << object->m_intValue << "\n";
break;
case gmlDoubleValue:
os << " " << object->m_doubleValue << "\n";
break;
case gmlStringValue:
os << " \"" << object->m_stringValue << "\"\n";
break;
case gmlListBegin:
os << "\n";
output(os, object->m_pFirstSon, d+2);
break;
case gmlListEnd:
break;
case gmlKey:
break;
case gmlEOF:
break;
case gmlError:
break;
}
}
}
} // end namespace ogdf