Clover coverage report - Maven Clover report
Coverage timestamp: Tue Sep 16 2008 01:16:37 EEST
file stats: LOC: 591   Methods: 20
NCLOC: 387   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
WsmlDLValidator.java 0% 0% 0% 0%
coverage
 1    /*
 2    wsmo4j - a WSMO API and Reference Implementation
 3    Copyright (c) 2005, University of Innsbruck, Austria
 4    This library is free software; you can redistribute it and/or modify it under
 5    the terms of the GNU Lesser General Public License as published by the Free
 6    Software Foundation; either version 2.1 of the License, or (at your option)
 7    any later version.
 8    This library is distributed in the hope that it will be useful, but WITHOUT
 9    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 10    FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 11    details.
 12    You should have received a copy of the GNU Lesser General Public License along
 13    with this library; if not, write to the Free Software Foundation, Inc.,
 14    59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 15    */
 16    package org.deri.wsmo4j.validator;
 17   
 18   
 19    import java.util.*;
 20    import java.util.Map.Entry;
 21   
 22    import org.omwg.logicalexpression.*;
 23    import org.omwg.logicalexpression.terms.Term;
 24    import org.omwg.ontology.*;
 25    import org.wsmo.common.*;
 26    import org.wsmo.common.exception.*;
 27    import org.wsmo.factory.LogicalExpressionFactory;
 28    import org.wsmo.validator.ValidationError;
 29    import org.wsmo.validator.ValidationWarning;
 30   
 31   
 32    /**
 33    * Checks an ontology for wsml-dl validity.
 34    *
 35    * <pre>
 36    * Created on Aug 18, 2005
 37    * Committed by $Author: morcen $
 38    * $Source$,
 39    * </pre>
 40    *
 41    * @author Holger Lausen (holger.lausen@deri.org)
 42    * @author nathalie.steinmetz@deri.org
 43    * @version $Revision: 1946 $ $Date: 2007-04-02 15:13:28 +0300 (Mon, 02 Apr 2007) $
 44    */
 45    public class WsmlDLValidator
 46    extends WsmlFullValidator {
 47   
 48    public List <Term> idConcepts = new Vector <Term> ();
 49   
 50    public List <Term> explicitConcepts = new Vector <Term> ();
 51   
 52    public List <Term> idInstances = new Vector <Term> ();
 53   
 54    public List <Term> explicitInstances = new Vector <Term> ();
 55   
 56    public List <Term> idRelations = new Vector <Term> ();
 57   
 58    public List <Term> explicitRelations = new Vector <Term> ();
 59   
 60    public List <Term> idAbstractRelations = new Vector <Term> ();
 61   
 62    public List <Term> idConcreteRelations = new Vector <Term> ();
 63   
 64    private WsmlDLExpressionValidator dlExprVal = null;
 65   
 66  0 public WsmlDLValidator(LogicalExpressionFactory leFactory) {
 67  0 super(leFactory);
 68    }
 69   
 70    /**
 71    * Checks if an axiom is valid to wsml-dl.
 72    *
 73    * @see org.deri.wsmo4j.validator.WsmlFullValidator#visitAxiom(org.omwg.ontology.Axiom)
 74    */
 75  0 protected void visitAxiom(Axiom axiom) {
 76  0 WsmlDLExpressionValidator dlExprVal = new WsmlDLExpressionValidator(axiom, leFactory, errors, this);
 77  0 WsmlFullExpressionValidator fullExprVal = new WsmlFullExpressionValidator(axiom, errors);
 78  0 FunctionSymbolHelper fsHelper = new FunctionSymbolHelper(
 79    axiom, errors, WSML.WSML_DL, this);
 80  0 IDCollectHelper idHelper = new IDCollectHelper();
 81  0 Iterator axioms = axiom.listDefinitions().iterator();
 82  0 while (axioms.hasNext()){
 83  0 LogicalExpression le = (LogicalExpression) axioms.next();
 84   
 85  0 le.accept(idHelper);
 86    // adding the Axiom's Concepts, Instances and Relations to the vectors
 87  0 idConcepts.addAll(idHelper.getConceptIds());
 88  0 explicitConcepts.addAll(idHelper.getExplicitConcepts());
 89  0 idInstances.addAll(idHelper.getInstanceIds());
 90  0 explicitInstances.addAll(idHelper.getExplicitInstances());
 91  0 idRelations.addAll(idHelper.getRelationIds());
 92  0 explicitRelations.addAll(idHelper.getExplicitRelations());
 93  0 idAbstractRelations.addAll(idHelper.getIdAbstractRelations());
 94  0 idConcreteRelations.addAll(idHelper.getIdConcreteRelations());
 95   
 96  0 le.accept(fsHelper);
 97   
 98  0 dlExprVal.setup();
 99  0 le.accept(fullExprVal);
 100  0 le.accept(dlExprVal);
 101    }
 102    }
 103   
 104    /**
 105    * Checks if a concept is valid to wsml-dl.
 106    *
 107    * @see org.deri.wsmo4j.validator.WsmlFullValidator#visitConcept(org.omwg.ontology.Concept)
 108    */
 109  0 protected void visitConcept(Concept concept) {
 110  0 super.visitConcept(concept);
 111  0 Iterator i = concept.listAttributes().iterator();
 112  0 while (i.hasNext()) {
 113  0 Attribute a = (Attribute)i.next();
 114  0 String id1 = a.getIdentifier().toString();
 115  0 String id2 = concept.getIdentifier().toString();
 116   
 117  0 if (a.getIdentifier() instanceof IRI) {
 118  0 id1 = ((IRI) a.getIdentifier()).getLocalName();
 119    }
 120  0 if (concept.getIdentifier() instanceof IRI) {
 121  0 id2 = ((IRI) concept.getIdentifier()).getLocalName();
 122    }
 123   
 124    // check if the attribute has the transitive feature
 125  0 if (a.isTransitive()) {
 126  0 addError(concept, a, ValidationError.ATTR_FEAT_ERR + ": Attributes may " +
 127    "not contain the attribute feature 'transitive' \n("
 128    + id1 + " at Concept " + id2 + ")");
 129    }
 130    // check if the attribute has the reflexive feature
 131  0 else if (a.isReflexive()) {
 132  0 addError(concept, a, ValidationError.ATTR_FEAT_ERR + ": Attributes may " +
 133    "not contain the attribute feature 'reflexive' \n("
 134    + id1 + " at Concept " + id2 + ")");
 135    }
 136    // check if the attribute has the symmetric feature
 137  0 else if (a.isSymmetric()) {
 138  0 addError(concept, a, ValidationError.ATTR_FEAT_ERR + ": Attributes may " +
 139    "not contain the attribute feature 'symmetric' \n("
 140    + id1 + " at Concept " + id2 + ")");
 141    }
 142    // check if the attribute has the inverse feature
 143  0 else if (a.getInverseOf() != null) {
 144  0 addError(concept, a, ValidationError.ATTR_FEAT_ERR + ": Attributes may " +
 145    "not contain the attribute feature 'inverseOf' \n("
 146    + id1 + " at Concept " + id2 + ")");
 147    }
 148    // check if an attribute has a cardinality constraint
 149  0 if (a.getMaxCardinality() != Integer.MAX_VALUE || a.getMinCardinality() != 0) {
 150  0 addError(concept, a, ValidationError.ATTR_CARD_ERR + ": Attributes may " +
 151    "not contain cardinality constraints \n("
 152    + id1 + " at Concept " + id2 + ")");
 153    }
 154  0 Iterator it = a.listTypes().iterator();
 155  0 while (it.hasNext()) {
 156  0 Type t = (Type)it.next();
 157    // check that if the attribute is constraining, it has a datatype range
 158  0 if (a.isConstraining()) {
 159  0 if (!(t instanceof WsmlDataType)) {
 160  0 addError(concept, a, ValidationError.ATTR_CONS_ERR
 161    + ": The attribute type 'ofType' is not allowed, "
 162    + "other than for datatype identifiers \n("
 163    + id1 + " at Concept " + id2 + ")");
 164    }
 165    }
 166    else {
 167  0 if (t instanceof Concept) {
 168  0 idConcepts.add(((Concept) t).getIdentifier());
 169    }
 170    }
 171    }
 172    // adding all attributes t o a vector
 173  0 idRelations.add(a.getIdentifier());
 174  0 isRelation(a);
 175    }
 176    // adding all concepts to a vector
 177  0 Identifier id = concept.getIdentifier();
 178  0 idConcepts.add(id);
 179  0 explicitConcepts.add(id);
 180  0 isConcept(concept);
 181   
 182    //checking for SuperConcepts and adding them to a vector
 183  0 if (!concept.listSuperConcepts().isEmpty()) {
 184  0 Iterator it = concept.listSuperConcepts().iterator();
 185  0 while (it.hasNext()) {
 186  0 Concept con = (Concept) it.next();
 187  0 idConcepts.add(con.getIdentifier());
 188  0 isConcept(con);
 189    }
 190    }
 191    }
 192   
 193    /*
 194    * Check for metamodelling error
 195    */
 196  0 private void isConcept(Entity e) {
 197  0 Identifier id = e.getIdentifier();
 198  0 if (idInstances.contains(id) || idRelations.contains(id) ||
 199    constants.isDataType(id.toString())){
 200  0 String idString = id.toString();
 201  0 if (id instanceof IRI) {
 202  0 idString = ((IRI) id).getLocalName();
 203    }
 204  0 addError(e, ValidationError.META_MODEL_ERR
 205    + ": An ID can only denote an entity of one single type: \n("
 206    + "at Concept " + idString + ")");
 207    }
 208    }
 209   
 210    /**
 211    * Checks if an instance is valid to wsml-dl.
 212    *
 213    * @see org.deri.wsmo4j.validator.WsmlFullValidator#visitInstance(org.omwg.ontology.Instance)
 214    */
 215  0 protected void visitInstance(Instance instance) {
 216  0 super.visitInstance(instance);
 217   
 218    // adding all instances to a vector
 219  0 Identifier id = instance.getIdentifier();
 220  0 idInstances.add(id);
 221  0 explicitInstances.add(id);
 222  0 Set entries = instance.listAttributeValues().entrySet();
 223  0 Iterator it = entries.iterator();
 224  0 while (it.hasNext()) {
 225  0 Entry entry = (Entry) it.next();
 226  0 Identifier attrId = (Identifier) entry.getKey();
 227  0 idRelations.add(attrId);
 228  0 Set valueSet = (Set) entry.getValue();
 229  0 Iterator it2 = valueSet.iterator();
 230  0 while (it2.hasNext()) {
 231  0 Value value = (Value) it2.next();
 232  0 if (value instanceof Instance) {
 233  0 idInstances.add(((Instance) value).getIdentifier());
 234  0 isInstance((Instance) value);
 235    }
 236    }
 237    }
 238  0 isInstance(instance);
 239    }
 240   
 241    /*
 242    * Check for metamodelling error
 243    */
 244  0 private void isInstance(Entity e) {
 245  0 Identifier id = e.getIdentifier();
 246  0 if (idConcepts.contains(id) || idRelations.contains(id) ||
 247    constants.isDataType(id.toString())){
 248  0 String idString = id.toString();
 249  0 if (id instanceof IRI) {
 250  0 idString = ((IRI) id).getLocalName();
 251    }
 252  0 addError(e, ValidationError.META_MODEL_ERR
 253    + ": An ID can only denote an entity of one single type: \n("
 254    + "at Instance " + idString + ")");
 255    }
 256    }
 257   
 258    /**
 259    * Checks if a relation is valid to wsml-dl.
 260    *
 261    * @see org.deri.wsmo4j.validator.WsmlFullValidator#visitRelation(org.omwg.ontology.Relation)
 262    */
 263  0 protected void visitRelation(Relation relation) {
 264  0 super.visitRelation(relation);
 265   
 266    // check if the relation is a binary relation
 267  0 if (relation.listParameters().size() != 2) {
 268  0 addError(relation, ValidationError.REL_ARITY_ERR + ": The arity of " +
 269    "relations is restricted to 2 \n");
 270    }
 271    else {
 272  0 Parameter p1 = relation.getParameter((byte)0);
 273  0 Parameter p2 = relation.getParameter((byte)1);
 274   
 275    // check that the first parameter hasn't got a datatype at its range
 276  0 Iterator it = p1.listTypes().iterator();
 277  0 while (it.hasNext()) {
 278  0 Type type = (Type) it.next();
 279  0 if (type instanceof WsmlDataType) {
 280  0 addError(relation, ValidationError.REL_ERR + ": The range of " +
 281    "the first parameter of a binary relation may not contain a " +
 282    "datatype\n");
 283    }
 284    else {
 285  0 idConcepts.add(((Concept) type).getIdentifier());
 286  0 isConcept((Concept) type);
 287    }
 288    }
 289   
 290    // check that the first parameter isn't constraining
 291  0 if (p1.isConstraining()) {
 292  0 addError(relation, ValidationError.REL_CONS_ERR + ": The 'ofType' " +
 293    "keyword is not allowed for the first parameter of a binary " +
 294    "relation");
 295    }
 296   
 297    /*
 298    * check that in case the second parameter is constraining, it
 299    * must have a datatype at its range
 300    */
 301  0 it = p2.listTypes().iterator();
 302  0 while (it.hasNext()) {
 303  0 Type type = (Type)it.next();
 304  0 if (p2.isConstraining()) {
 305  0 if (!(type instanceof WsmlDataType)) {
 306  0 addError(relation, ValidationError.REL_CONS_ERR + ": The 'ofType' " +
 307    "keyword is not allowed without a datatype at the parameter's " +
 308    "range\n");
 309    }
 310    }
 311    else {
 312  0 if (type instanceof Concept) {
 313  0 idConcepts.add(((Concept) type).getIdentifier());
 314  0 isConcept((Concept) type);
 315    }
 316    }
 317    }
 318    }
 319    // adding all relations to a vector
 320  0 idRelations.add(relation.getIdentifier());
 321  0 explicitRelations.add(relation.getIdentifier());
 322  0 isRelation(relation);
 323   
 324    // checking for SuperRelations and adding them to a vector
 325  0 if (!relation.listSuperRelations().isEmpty()) {
 326  0 Iterator it = relation.listSuperRelations().iterator();
 327  0 while (it.hasNext()) {
 328  0 Relation rel = (Relation) it.next();
 329  0 idRelations.add(rel.getIdentifier());
 330  0 isRelation(rel);
 331    }
 332    }
 333    }
 334   
 335    /*
 336    * Check for metamodelling error
 337    */
 338  0 private void isRelation(Entity e) {
 339  0 Identifier id = e.getIdentifier();
 340  0 if (idConcepts.contains(id) || idInstances.contains(id) ||
 341    constants.isDataType(id.toString())){
 342  0 String idString = id.toString();
 343  0 if (id instanceof IRI) {
 344  0 idString = ((IRI) id).getLocalName();
 345    }
 346  0 addError(e, ValidationError.META_MODEL_ERR
 347    + ": An ID can only denote an entity of one single type: \n("
 348    + "at Relation " + idString + ")");
 349    }
 350    }
 351   
 352    /**
 353    * Checks if a relation instance is valid to wsml-dl.
 354    *
 355    * @throws InvalidModelException
 356    * @throws SynchronisationException
 357    * @see org.deri.wsmo4j.validator.WsmlFullValidator#visitRelationInstance(org.omwg.ontology.RelationInstance)
 358    */
 359  0 protected void visitRelationInstance(RelationInstance relationInstance)
 360    throws SynchronisationException, InvalidModelException {
 361    /*
 362    * It is not possible to check if the values of a relation correspond to
 363    * their signatures, because instance hierarches are not implemented
 364    *
 365    * Exceptions from relationInstance.getParameterValue(byte) shouldn't be
 366    * thrown
 367    */
 368  0 super.visitRelationInstance(relationInstance);
 369   
 370  0 Value v1 = relationInstance.getParameterValue((byte)0);
 371  0 Value v2 = relationInstance.getParameterValue((byte)1);
 372  0 String id1 = relationInstance.getIdentifier().toString();
 373  0 String id2 = relationInstance.getRelation().getIdentifier().toString();
 374  0 if (relationInstance.getIdentifier() instanceof IRI) {
 375  0 id1 = ((IRI) relationInstance.getIdentifier()).getLocalName();
 376    }
 377  0 if (relationInstance.getRelation().getIdentifier() instanceof IRI) {
 378  0 id2 = ((IRI) relationInstance.getRelation().getIdentifier()).getLocalName();
 379    }
 380    // check that both values of the relation are specified
 381  0 if (v1 == null || v2 == null) {
 382   
 383  0 addError(relationInstance, ValidationError.REL_INST_ERR + ": Both values of " +
 384    "the relation have to be specified \n("
 385    + id1 + " at relation "
 386    + id2 + ")");
 387    }
 388    // check that the first value is not a data value
 389  0 if (v1 instanceof DataValue) {
 390  0 addError(relationInstance, ValidationError.REL_INST_ERR + ": The first value " +
 391    "may not be a data value \n(" + id1 + " at relation " + id2 + ")");
 392    }
 393  0 idRelations.add(relationInstance.getIdentifier());
 394  0 isRelation(relationInstance);
 395    }
 396   
 397    /**
 398    * @return Returns the idConcepts.
 399    */
 400  0 public List getIdConcepts() {
 401  0 return idConcepts;
 402    }
 403   
 404    /**
 405    * @return Returns the idInstances.
 406    */
 407  0 public List getIdInstances() {
 408  0 return idInstances;
 409    }
 410   
 411    /**
 412    * @return Returns the idRelations.
 413    */
 414  0 public List getIdRelations() {
 415  0 return idRelations;
 416    }
 417   
 418    /**
 419    *
 420    * @return Returns the idAbstractRelations
 421    */
 422  0 public List getIdAbstractRelations() {
 423  0 return idAbstractRelations;
 424    }
 425   
 426    /**
 427    * @return Returns the idConcreteRelations
 428    */
 429  0 public List getIdConcreteRelations() {
 430  0 return idConcreteRelations;
 431    }
 432   
 433  0 private void checkForMetaModelling() {
 434  0 Iterator it = idConcepts.iterator();
 435  0 while (it.hasNext()) {
 436  0 Object obj = it.next();
 437  0 if (obj instanceof IRI) {
 438  0 Identifier id = (Identifier) obj;
 439  0 if (idInstances.contains(id) || idRelations.contains(id)
 440    || constants.isDataType(id.toString())) {
 441  0 String idString = id.toString();
 442  0 if (id instanceof IRI) {
 443  0 idString = ((IRI) id).getLocalName();
 444    }
 445  0 addError(null, ValidationError.META_MODEL_ERR
 446    + ": An ID can only denote an entity of one single type: \n("
 447    + "at Concept " + idString + ")");
 448    }
 449    }
 450    }
 451  0 it = idInstances.iterator();
 452  0 while (it.hasNext()) {
 453  0 Object obj = it.next();
 454  0 if (obj instanceof IRI) {
 455  0 Identifier id = (Identifier) obj;
 456  0 if (idConcepts.contains(id) || idRelations.contains(id)
 457    || constants.isDataType(id.toString())) {
 458  0 String idString = id.toString();
 459  0 if (id instanceof IRI) {
 460  0 idString = ((IRI) id).getLocalName();
 461    }
 462  0 addError(null, ValidationError.META_MODEL_ERR
 463    + ": An ID can only denote an entity of one single type: \n("
 464    + "at Instance " + idString + ")");
 465    }
 466    }
 467    }
 468  0 it = idRelations.iterator();
 469  0 while (it.hasNext()) {
 470  0 Object obj = it.next();
 471  0 if (obj instanceof IRI) {
 472  0 Identifier id = (Identifier) obj;
 473  0 if (idInstances.contains(id) || idConcepts.contains(id)
 474    || constants.isDataType(id.toString())) {
 475  0 String idString = id.toString();
 476  0 if (id instanceof IRI) {
 477  0 idString = ((IRI) id).getLocalName();
 478    }
 479  0 addError(null, ValidationError.META_MODEL_ERR
 480    + ": An ID can only denote an entity of one single type: \n("
 481    + "at Relation " + idString + ")");
 482    }
 483    }
 484    }
 485    }
 486   
 487  0 public boolean isValid(TopEntity te, List <ValidationError> errorMessages, List <ValidationWarning> warningMessages) {
 488  0 boolean result = super.isValid(te, errorMessages, warningMessages);
 489   
 490  0 checkForMetaModelling();
 491   
 492    // check if all concepts have been explicitly declared
 493  0 for (int i=0; i<idConcepts.size(); i++) {
 494  0 String id = idConcepts.get(i).toString();
 495   
 496  0 if (idConcepts.get(i) instanceof IRI) {
 497  0 id = ((IRI) idConcepts.get(i)).getLocalName();
 498    }
 499   
 500  0 if (!explicitConcepts.contains(idConcepts.get(i))) {
 501  0 addWarning(te, "concept " + id + " not explicitly declared!", null);
 502    }
 503    }
 504   
 505    // // check if all instances have been explicitly declared
 506    // for (int i=0; i<idInstances.size(); i++) {
 507    // String id = idInstances.get(i).toString();
 508    //
 509    // if (idInstances.get(i) instanceof IRI) {
 510    // id = ((IRI) idInstances.get(i)).getLocalName();
 511    // }
 512    //
 513    // if (!explicitInstances.contains(idInstances.get(i))) {
 514    // addWarning(te, "instance " + id + " not explicitly declared!", null);
 515    // }
 516    // }
 517   
 518    // // check if all relations have been explicitly declared
 519    // for (int i=0; i<idRelations.size(); i++) {
 520    // String id = idRelations.get(i).toString();
 521    //
 522    // if (idRelations.get(i) instanceof IRI) {
 523    // id = ((IRI) idRelations.get(i)).getLocalName();
 524    // }
 525    //
 526    // if (!explicitRelations.contains(idRelations.get(i))) {
 527    // addWarning(te, "relation " + id + " not explicitly declared!", null);
 528    // }
 529    // }
 530   
 531  0 return result;
 532    }
 533   
 534  0 public boolean isValid(LogicalExpression logExpr, List <ValidationError> errorMessages, List <ValidationWarning> warningMessages) {
 535  0 super.isValid(logExpr, errorMessages, warningMessages);
 536  0 errors = errorMessages;
 537  0 warnings = warningMessages;
 538   
 539  0 dlExprVal = new WsmlDLExpressionValidator(null, leFactory, errors, this);
 540  0 WsmlFullExpressionValidator fullExprVal = new WsmlFullExpressionValidator(null, errors);
 541  0 FunctionSymbolHelper fsHelper = new FunctionSymbolHelper(
 542    null, errors, WSML.WSML_DL, this);
 543  0 IDCollectHelper idHelper = new IDCollectHelper();
 544   
 545  0 logExpr.accept(idHelper);
 546    // adding the Axiom's Concepts, Instances and Relations to the vectors
 547  0 idConcepts.addAll(idHelper.getConceptIds());
 548  0 explicitConcepts.addAll(idHelper.getExplicitConcepts());
 549  0 idInstances.addAll(idHelper.getInstanceIds());
 550  0 explicitInstances.addAll(idHelper.getExplicitInstances());
 551  0 idRelations.addAll(idHelper.getRelationIds());
 552  0 explicitRelations.addAll(idHelper.getExplicitRelations());
 553  0 idAbstractRelations.addAll(idHelper.getIdAbstractRelations());
 554  0 idConcreteRelations.addAll(idHelper.getIdConcreteRelations());
 555   
 556  0 logExpr.accept(fsHelper);
 557   
 558  0 dlExprVal.setup();
 559  0 logExpr.accept(fullExprVal);
 560  0 logExpr.accept(dlExprVal);
 561   
 562  0 checkForMetaModelling();
 563   
 564  0 return (errors.isEmpty());
 565    }
 566   
 567  0 public Variable getRootVariable(LogicalExpression logExpr) {
 568  0 return dlExprVal.getRoot();
 569    }
 570   
 571    /*
 572    * Adds a new ValidationError to the error list
 573    */
 574  0 protected void addError(Entity ent, String msg) {
 575  0 ValidationErrorImpl ve = new ValidationErrorImpl(ent, msg, WSML.WSML_DL);
 576  0 if (!errors.contains(ve)) {
 577  0 errors.add(ve);
 578    }
 579    }
 580   
 581    /*
 582    * Adds a new AttributeError to the error list
 583    */
 584  0 protected void addError(Entity ent, Attribute att, String msg) {
 585  0 AttributeErrorImpl ae = new AttributeErrorImpl(ent, att, msg, WSML.WSML_DL);
 586  0 if (!errors.contains(ae)) {
 587  0 errors.add(ae);
 588    }
 589    }
 590   
 591    }