From 4d2765927aca9ba98ca0b4026a9bcadb175add56 Mon Sep 17 00:00:00 2001 From: lempinen Date: Fri, 14 Oct 2011 14:03:10 +0000 Subject: [PATCH] More Validations and separated validations for dependencies and expressions git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@22745 ac1ea38d-2e2b-0410-8846-a27921b304fc --- org.simantics.sysdyn.ontology/graph.tg | Bin 69506 -> 70118 bytes .../graph/Validation.pgraph | 34 ++- .../org/simantics/sysdyn/SysdynResource.java | 48 +++- org.simantics.sysdyn.ui/plugin.xml | 7 +- .../simantics/sysdyn/ui/utils/ModelUtils.java | 8 +- .../ui/validation/DependencyFunction.java | 240 +++++++----------- .../validation/ExpressionIssueFunction.java | 151 +++++++++++ .../validation/NoSuchVariableException.java | 6 + .../ui/validation/SyntaxErrorException.java | 7 + .../UndefinedExpressionException.java | 6 + .../UnsupportedCharactersException.java | 6 + .../sysdyn/ui/validation/ValidationUtils.java | 152 +++++++++++ 12 files changed, 489 insertions(+), 176 deletions(-) create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ExpressionIssueFunction.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/NoSuchVariableException.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/SyntaxErrorException.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/UndefinedExpressionException.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/UnsupportedCharactersException.java create mode 100644 org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ValidationUtils.java diff --git a/org.simantics.sysdyn.ontology/graph.tg b/org.simantics.sysdyn.ontology/graph.tg index a8cc35be2a275f1e0408f3753597ee9c3ce081b4..14b2d4f329d56191a881bd8a7dadcd0dc24db9af 100644 GIT binary patch literal 70118 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 literal 69506 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 - + + + + + + diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ModelUtils.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ModelUtils.java index cfa0f15f..3ce5b532 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ModelUtils.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/utils/ModelUtils.java @@ -73,7 +73,13 @@ public class ModelUtils { ); GraphUtils.create2(g, - sr.Validations_DependencyConnectionsIssueSource, + sr.Validations_Dependencies_DependencyConnectionsIssueSource, + L0X.IsActivatedBy, model, + l0.PartOf, model + ); + + GraphUtils.create2(g, + sr.Validations_Expressions_ExpressionIssueSource, L0X.IsActivatedBy, model, l0.PartOf, model ); diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/DependencyFunction.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/DependencyFunction.java index edd32658..a6431ad4 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/DependencyFunction.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/DependencyFunction.java @@ -1,44 +1,35 @@ package org.simantics.sysdyn.ui.validation; -import java.io.StringReader; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Calendar; import java.util.Collection; import java.util.HashSet; -import java.util.List; import java.util.Set; -import java.util.UUID; import org.simantics.databoard.Bindings; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.WriteGraph; -import org.simantics.db.common.request.ObjectsWithType; -import org.simantics.db.common.utils.OrderedSetUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.issues.ontology.IssueResource; import org.simantics.layer0.Layer0; import org.simantics.scl.reflection.annotations.SCLValue; import org.simantics.simulation.ontology.SimulationResource; import org.simantics.sysdyn.SysdynResource; -import org.simantics.sysdyn.expressionParser.ExpressionParser; -import org.simantics.sysdyn.expressionParser.ParseException; -import org.simantics.sysdyn.expressionParser.TokenMgrError; public class DependencyFunction { private static String getMissingLinkLabel(String name, String dependency) throws DatabaseException { - String label = "Missing link " + dependency + " in " + name; - return label; + return "Missing link " + dependency + " in " + name; } - private static String getUnusedDependencyLabel(String name, String dependency) throws DatabaseException { - String label = "Unused dependency " + dependency + " in " + name; - return label; + private static String getNoSuchVariableLabel(String name, String dependency) throws DatabaseException { + return name + " refers to variable " + dependency + " that does not exist"; } + private static String getUnusedDependencyLabel(String name, String dependency) throws DatabaseException { + return "Unused dependency " + dependency + " in " + name; + } + /** * * One issue is enough. The first encounter of a new issue returns Boolean.FALSE. @@ -55,38 +46,51 @@ public class DependencyFunction { ReadGraph graph = (ReadGraph)_graph; Resource variable = (Resource)_resource; Collection existing = (Collection)_existing; - - Set references = getReferences(graph, variable); - Set dependencies = getDependencies(graph, variable); + if(!graph.hasStatement(variable) || !graph.hasStatement(variable, Layer0.getInstance(graph).PartOf)) + return Boolean.FALSE; + String name = graph.getRelatedValue(variable, Layer0.getInstance(graph).HasName, Bindings.STRING); - - for(String reference : references) { - if(!dependencies.contains(reference) && match(graph, existing, variable, getMissingLinkLabel(name, reference)) == null) - return Boolean.FALSE; - } - - for(String dependency : dependencies) { - if(!references.contains(dependency) && match(graph, existing, variable, getUnusedDependencyLabel(name, dependency)) == null) - return Boolean.FALSE; + + Set references; + Set dependencies; + try { + references = ValidationUtils.getReferences(graph, variable); + dependencies = ValidationUtils.getDependencies(graph, variable); + + + for(String reference : references) { + if(!dependencies.contains(reference) && match(graph, existing, variable, getMissingLinkLabel(name, reference)) == null) + return Boolean.FALSE; + } + + for(String dependency : dependencies) { + if(!references.contains(dependency) && match(graph, existing, variable, getUnusedDependencyLabel(name, dependency)) == null) + return Boolean.FALSE; + } + } catch (SyntaxErrorException e) { + } catch (UnsupportedCharactersException e) { + } catch (UndefinedExpressionException e) { } - - /* - * See if there are any unnecessary issues in existing - * - * FIXME: Currently there are no other issues, so this can check ALL issues. - */ IssueResource ISSUE = IssueResource.getInstance(graph); - + SysdynResource sr = SysdynResource.getInstance(graph); + Layer0 l0 = Layer0.getInstance(graph); + + Resource context; for(Resource exist : existing) { - if(variable.equals(graph.getSingleObject(exist, ISSUE.HasIssueContext))) { + context = graph.getSingleObject(exist, ISSUE.HasIssueContext); + if(!graph.hasStatement(context) || graph.hasStatement(context, l0.PartOf)) + return Boolean.FALSE; + if(variable.equals(context) && graph.isInstanceOf(exist, sr.DependencyIssue)) { return Boolean.FALSE; } } + return Boolean.TRUE; } + @SCLValue(type = "a -> b -> c -> d -> e -> f") public static Object dependencySynchronizer(Object _graph, Object _resource, Object _source, Object _model, Object _existing) throws DatabaseException { @@ -96,87 +100,72 @@ public class DependencyFunction { Resource source = (Resource)_source; Collection existing = (Collection)_existing; Layer0 L0 = Layer0.getInstance(graph); - - Set references = getReferences(graph, variable); - Set dependencies = getDependencies(graph, variable); - + IssueResource ISSUE = IssueResource.getInstance(graph); + SysdynResource sr = SysdynResource.getInstance(graph); String name = graph.getRelatedValue(variable, L0.HasName, Bindings.STRING); - + Set labels = new HashSet(); String label; - for(String reference : references) { - label = getMissingLinkLabel(name, reference); - if(!dependencies.contains(reference)) { - labels.add(label); - if(match(graph, existing, variable, label) == null) { - createIssue(graph, model, source, variable, label); - } - } - /*else { - if((issue = match(graph, existing, variable, label)) != null) { - removeIssue(graph, model, source, variable, issue, existing); - } - }*/ - } - - for(String dependency : dependencies) { - label = getUnusedDependencyLabel(name, dependency); - if(!references.contains(dependency)) { - labels.add(label); - if(match(graph, existing, variable, label) == null) { - createIssue(graph, model, source, variable, label); - } + Set references; + Set dependencies; + try { + references = ValidationUtils.getReferences(graph, variable); + dependencies = ValidationUtils.getDependencies(graph, variable); + for(String reference : references) { + if(!dependencies.contains(reference)) { + if(ValidationUtils.isReachable(graph, variable, reference)) { + label = getMissingLinkLabel(name, reference); + labels.add(label); + if(match(graph, existing, variable, label) == null) { + ValidationUtils.createIssue(graph, model, source, variable, label, ISSUE.Severity_Warning, sr.DependencyIssue); + } + } else { + label = getNoSuchVariableLabel(name, reference); + labels.add(label); + if(match(graph, existing, variable, label) == null) { + ValidationUtils.createIssue(graph, model, source, variable, label, ISSUE.Severity_Error, sr.DependencyIssue); + } + } + } } - - /*else { - if((issue = match(graph, existing, variable, label)) != null) { - removeIssue(graph, model, source, variable, issue, existing); + + for(String dependency : dependencies) { + label = getUnusedDependencyLabel(name, dependency); + if(!references.contains(dependency)) { + labels.add(label); + if(match(graph, existing, variable, label) == null) { + ValidationUtils.createIssue(graph, model, source, variable, label, ISSUE.Severity_Warning, sr.DependencyIssue); + } } - }*/ + } + } catch (SyntaxErrorException e) { + } catch (UnsupportedCharactersException e) { + } catch (UndefinedExpressionException e) { } - + Set toBeRemoved = new HashSet(); + Resource context; for(Resource exist : existing) { - String l = graph.getRelatedValue(exist, L0.HasLabel); - Resource i = graph.getSingleObject(exist, IssueResource.getInstance(graph).HasIssueContext); - if(variable.equals(i) && !labels.contains(l)) + context = graph.getSingleObject(exist, ISSUE.HasIssueContext); + if(!graph.hasStatement(context) || !graph.hasStatement(context, L0.PartOf)) toBeRemoved.add(exist); + if(graph.isInstanceOf(exist, sr.DependencyIssue) && variable.equals(context)) { + String l = graph.getRelatedValue(exist, L0.HasLabel); + if(!labels.contains(l)) + toBeRemoved.add(exist); + } } - + for(Resource r : toBeRemoved) { - removeIssue(graph, model, source, variable, r, existing); + ValidationUtils.removeIssue(graph, model, source, variable, r, existing); } - + return Boolean.TRUE; } - - private static Resource createIssue(WriteGraph graph, Resource model, Resource source, Resource variable, String label) throws DatabaseException { - Layer0 L0 = Layer0.getInstance(graph); - IssueResource ISSUE = IssueResource.getInstance(graph); - - Resource issue = graph.newResource(); - graph.claim(issue, L0.InstanceOf, null, ISSUE.Issue); - graph.claim(issue, ISSUE.HasIssueContext, null, variable); - graph.claim(issue, ISSUE.HasSeverity, ISSUE.Severity_Warning); - graph.claimLiteral(issue, L0.HasLabel, label, Bindings.STRING); - graph.claimLiteral(issue, L0.HasName, UUID.randomUUID().toString(), Bindings.STRING); - DateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); - String created = format.format(Calendar.getInstance().getTime()); - graph.claimLiteral(issue, ISSUE.HasCreationTime, created, Bindings.STRING); - graph.claim(source, ISSUE.Manages, issue); - graph.claim(model, L0.ConsistsOf, issue); - return issue; - } - - private static void removeIssue(WriteGraph graph, Resource model, Resource source, Resource variable, Resource issue, Collection existing) throws DatabaseException { - graph.deny(issue, Layer0.getInstance(graph).PartOf); - graph.deny(source, IssueResource.getInstance(graph).Manages, issue); - existing.remove(issue); - } @SCLValue(type = "a -> b -> c") public static Object dependencyBaseRealizationFunction(Object _graph, Object _model) throws DatabaseException { @@ -198,58 +187,5 @@ public class DependencyFunction { } return null; } - - // Returns the names of the related variables (dependencies) - private static HashSet getDependencies(ReadGraph graph, Resource r) throws DatabaseException { - HashSet variables = new HashSet(); - if(graph != null && r != null) { - SysdynResource sr = SysdynResource.getInstance(graph); - Layer0 l0 = Layer0.getInstance(graph); - - Collection dependencies = graph.getObjects(r, sr.IsHeadOf); - - for(Resource d : dependencies) { - if(graph.isInstanceOf(d, sr.Dependency)) { - Resource tail = graph.getPossibleObject(d, sr.HasTail); - if(tail != null) { - Object name = graph.getPossibleRelatedValue(tail, l0.HasName); - if(name != null) - variables.add((String)name); - } - } - } - } - return variables; - } - - private static HashSet getReferences(ReadGraph graph, Resource r) throws DatabaseException { - HashSet references = new HashSet(); - ExpressionParser parser = new ExpressionParser(new StringReader("")); - SysdynResource sr = SysdynResource.getInstance(graph); - Layer0 l0 = Layer0.getInstance(graph); - Resource hasExpressions = graph.getPossibleObject(r, sr.HasExpressions); - if(hasExpressions != null){ - List expressionList = OrderedSetUtils.toList(graph, hasExpressions); - for(Resource expression : expressionList) { - for(Resource s : graph.syncRequest(new ObjectsWithType(expression, sr.HasEquation, l0.String))) { - String value = graph.getValue(s, Bindings.STRING); - parser.ReInit(new StringReader(value)); - try { - parser.expr(); - references.addAll(parser.getReferences().keySet()); - } catch (ParseException e1) { - // TODO: Issue - System.out.println("SYNTAX ERROR"); -// ef.setSyntaxError(e1.currentToken, "Syntax Error"); - } catch (TokenMgrError err) { - // TODO: Issue - System.out.println("UNSUPPORTED CHARACTERS"); -// ef.setSyntaxError(0, textString.length(), ExpressionField.SYNTAX_ERROR, "Expression contains unsupported characters"); - } - } - } - } - return references; - } } diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ExpressionIssueFunction.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ExpressionIssueFunction.java new file mode 100644 index 00000000..344310fb --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ExpressionIssueFunction.java @@ -0,0 +1,151 @@ +package org.simantics.sysdyn.ui.validation; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.exception.DatabaseException; +import org.simantics.issues.ontology.IssueResource; +import org.simantics.layer0.Layer0; +import org.simantics.scl.reflection.annotations.SCLValue; +import org.simantics.simulation.ontology.SimulationResource; +import org.simantics.sysdyn.SysdynResource; + +/** + * Detects syntax errors, unsupported characters and undefined expressions from variables. + * + * @author tlteemu + * + */ +public class ExpressionIssueFunction { + + private static String getSyntaxErrorLabel(String name) throws DatabaseException { + return "Syntax error in " + name; + } + + private static String getUnsupportedCharacterLabel(String name) throws DatabaseException { + return "Unsupported character(s) in " + name; + } + + private static String getUndefinedExpressionLabel(String name) throws DatabaseException { + return "Undefined expression in " + name; + } + + @SCLValue(type = "a -> b -> c -> d") + public static Object expressionValidator(Object _graph, Object _resource, Object _existing) throws DatabaseException { + + ReadGraph graph = (ReadGraph)_graph; + Resource variable = (Resource)_resource; + Collection existing = (Collection)_existing; + String name = graph.getRelatedValue(variable, Layer0.getInstance(graph).HasName, Bindings.STRING); + + // Try if there are any errors while parsing the expressions + try { + ValidationUtils.getReferences(graph, variable); + } catch (SyntaxErrorException e) { + if(match(graph, existing, variable, getSyntaxErrorLabel(name)) == null) + return Boolean.FALSE; + } catch (UnsupportedCharactersException e) { + if(match(graph, existing, variable, getUnsupportedCharacterLabel(name)) == null) + return Boolean.FALSE; + } catch (UndefinedExpressionException e) { + if(match(graph, existing, variable, getUndefinedExpressionLabel(name)) == null) + return Boolean.FALSE; + } + + SysdynResource sr = SysdynResource.getInstance(graph); + IssueResource ISSUE = IssueResource.getInstance(graph); + Layer0 l0 = Layer0.getInstance(graph); + + Resource context; + // There were no errors -> There should be no expression issues for this variable + for(Resource exist : existing) { + context = graph.getSingleObject(exist, ISSUE.HasIssueContext); + if(!graph.hasStatement(context) || graph.hasStatement(context, l0.PartOf)) + return Boolean.FALSE; + if(variable.equals(context) && graph.isInstanceOf(exist, sr.ExpressionIssue)) { + return Boolean.FALSE; + } + } + + + return Boolean.TRUE; + } + + + @SCLValue(type = "a -> b -> c -> d -> e -> f") + public static Object expressionSynchronizer(Object _graph, Object _resource, Object _source, Object _model, Object _existing) throws DatabaseException { + WriteGraph graph = (WriteGraph)_graph; + Resource variable = (Resource)_resource; + Resource model = (Resource)_model; + Resource source = (Resource)_source; + Collection existing = (Collection)_existing; + Layer0 L0 = Layer0.getInstance(graph); + IssueResource ISSUE = IssueResource.getInstance(graph); + SysdynResource sr = SysdynResource.getInstance(graph); + + String name = graph.getRelatedValue(variable, L0.HasName, Bindings.STRING); + String label = null; + Resource issue = null; + try { + ValidationUtils.getReferences(graph, variable); + } catch (SyntaxErrorException e) { + label = getSyntaxErrorLabel(name); + if(match(graph, existing, variable, label) == null) + issue = ValidationUtils.createIssue(graph, model, source, variable, label, ISSUE.Severity_Error, sr.ExpressionIssue); + } catch (UnsupportedCharactersException e) { + label = getUnsupportedCharacterLabel(name); + if(match(graph, existing, variable, label) == null) + issue = ValidationUtils.createIssue(graph, model, source, variable, label, ISSUE.Severity_Error, sr.ExpressionIssue); + } catch (UndefinedExpressionException e) { + label = getUndefinedExpressionLabel(name); + if(match(graph, existing, variable, label) == null) + issue = ValidationUtils.createIssue(graph, model, source, variable, label, ISSUE.Severity_Error, sr.ExpressionIssue); + } + + Resource context; + Set toBeRemoved = new HashSet(); + for(Resource exist : existing) { + context = graph.getSingleObject(exist, ISSUE.HasIssueContext); + if(!graph.hasStatement(context) || !graph.hasStatement(context, L0.PartOf)) + toBeRemoved.add(exist); + else if(!exist.equals(issue) && graph.isInstanceOf(exist, sr.ExpressionIssue) && variable.equals(context)) { + String l = graph.getRelatedValue(exist, L0.HasLabel); + if(!l.equals(label)) { + toBeRemoved.add(exist); + } + } + } + + for(Resource r : toBeRemoved) { + ValidationUtils.removeIssue(graph, model, source, variable, r, existing); + } + + return Boolean.TRUE; + } + + + @SCLValue(type = "a -> b -> c") + public static Object expressionBaseRealizationFunction(Object _graph, Object _model) throws DatabaseException { + ReadGraph graph = (ReadGraph)_graph; + Resource model = (Resource)_model; + return graph.getSingleObject(model, SimulationResource.getInstance(graph).HasConfiguration); + } + + + private static Resource match(ReadGraph graph, Collection existing, Resource variable, String description) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + IssueResource ISSUE = IssueResource.getInstance(graph); + for(Resource exist : existing) { + Resource source = graph.getSingleObject(exist, ISSUE.HasIssueContext); + String desc = graph.getRelatedValue(exist, L0.HasLabel); + if(source.equals(variable) && desc.equals(description)) return exist; + } + return null; + } + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/NoSuchVariableException.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/NoSuchVariableException.java new file mode 100644 index 00000000..668ddee4 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/NoSuchVariableException.java @@ -0,0 +1,6 @@ +package org.simantics.sysdyn.ui.validation; + +public class NoSuchVariableException extends RuntimeException { + private static final long serialVersionUID = -5766352512554068379L; + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/SyntaxErrorException.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/SyntaxErrorException.java new file mode 100644 index 00000000..8d577c98 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/SyntaxErrorException.java @@ -0,0 +1,7 @@ +package org.simantics.sysdyn.ui.validation; + +public class SyntaxErrorException extends RuntimeException { + private static final long serialVersionUID = -6466653179001958636L; + + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/UndefinedExpressionException.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/UndefinedExpressionException.java new file mode 100644 index 00000000..bffa074e --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/UndefinedExpressionException.java @@ -0,0 +1,6 @@ +package org.simantics.sysdyn.ui.validation; + +public class UndefinedExpressionException extends RuntimeException { + private static final long serialVersionUID = 7352486116119189105L; + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/UnsupportedCharactersException.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/UnsupportedCharactersException.java new file mode 100644 index 00000000..e96b6096 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/UnsupportedCharactersException.java @@ -0,0 +1,6 @@ +package org.simantics.sysdyn.ui.validation; + +public class UnsupportedCharactersException extends RuntimeException { + private static final long serialVersionUID = 8210873686720503188L; + +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ValidationUtils.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ValidationUtils.java new file mode 100644 index 00000000..349e0303 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/validation/ValidationUtils.java @@ -0,0 +1,152 @@ +package org.simantics.sysdyn.ui.validation; + +import java.io.StringReader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.UUID; + +import org.simantics.databoard.Bindings; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.WriteGraph; +import org.simantics.db.common.request.ObjectsWithType; +import org.simantics.db.common.utils.OrderedSetUtils; +import org.simantics.db.exception.DatabaseException; +import org.simantics.issues.ontology.IssueResource; +import org.simantics.layer0.Layer0; +import org.simantics.sysdyn.SysdynResource; +import org.simantics.sysdyn.expressionParser.ExpressionParser; +import org.simantics.sysdyn.expressionParser.ParseException; +import org.simantics.sysdyn.expressionParser.TokenMgrError; + +public class ValidationUtils { + + public static void removeIssue(WriteGraph graph, Resource model, Resource source, Resource variable, Resource issue, Collection existing) throws DatabaseException { + graph.deny(issue, Layer0.getInstance(graph).PartOf); + graph.deny(source, IssueResource.getInstance(graph).Manages, issue); + existing.remove(issue); + } + + public static Resource createIssue(WriteGraph graph, Resource model, Resource source, Resource variable, String label) throws DatabaseException { + return createIssue(graph, model, source, variable, label, IssueResource.getInstance(graph).Severity_Error); + } + + + public static Resource createIssue(WriteGraph graph, Resource model, Resource source, Resource variable, String label, Resource severity) throws DatabaseException { + return createIssue(graph, model, source, variable, label, severity, SysdynResource.getInstance(graph).SysdynIssue); + } + + + public static Resource createIssue(WriteGraph graph, Resource model, Resource source, Resource variable, String label, Resource severity, Resource type) throws DatabaseException { + Layer0 L0 = Layer0.getInstance(graph); + IssueResource ISSUE = IssueResource.getInstance(graph); + + Resource issue = graph.newResource(); + graph.claim(issue, L0.InstanceOf, null, type); + graph.claim(issue, ISSUE.HasIssueContext, null, variable); + graph.claim(issue, ISSUE.HasSeverity, severity); + graph.claimLiteral(issue, L0.HasLabel, label, Bindings.STRING); + graph.claimLiteral(issue, L0.HasName, UUID.randomUUID().toString(), Bindings.STRING); + DateFormat format = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss"); + String created = format.format(Calendar.getInstance().getTime()); + graph.claimLiteral(issue, ISSUE.HasCreationTime, created, Bindings.STRING); + graph.claim(source, ISSUE.Manages, issue); + graph.claim(model, L0.ConsistsOf, issue); + return issue; + } + + public static HashSet getReferences(ReadGraph graph, Resource r) throws DatabaseException, SyntaxErrorException, UnsupportedCharactersException, UndefinedExpressionException { + HashSet references = new HashSet(); + ExpressionParser parser = new ExpressionParser(new StringReader("")); + + SysdynResource sr = SysdynResource.getInstance(graph); + Layer0 l0 = Layer0.getInstance(graph); + List expressionList = getExpressions(graph, r); + if(expressionList == null || expressionList.isEmpty()) + throw new UndefinedExpressionException(); + for(Resource expression : expressionList) { + for(Resource s : graph.syncRequest(new ObjectsWithType(expression, sr.HasEquation, l0.String))) { + String value = graph.getValue(s, Bindings.STRING); + if(value.length() == 0) + throw new UndefinedExpressionException(); + + parser.ReInit(new StringReader(value)); + try { + parser.expr(); + references.addAll(parser.getReferences().keySet()); + } catch (ParseException e1) { + throw new SyntaxErrorException(); + } catch (TokenMgrError err) { + throw new UnsupportedCharactersException(); + } + } + } + return references; + } + + private static List getExpressions(ReadGraph graph, Resource r) throws DatabaseException { + SysdynResource sr = SysdynResource.getInstance(graph); + Resource hasExpressions = graph.getPossibleObject(r, sr.HasExpressions); + if(hasExpressions != null) + return OrderedSetUtils.toList(graph, hasExpressions); + else + return null; + } + + + /** + * Returns the names of the related variables (dependencies) + * + * @param graph + * @param r + * @return + * @throws DatabaseException + */ + public static HashSet getDependencies(ReadGraph graph, Resource r) throws DatabaseException { + HashSet variables = new HashSet(); + if(graph != null && r != null) { + SysdynResource sr = SysdynResource.getInstance(graph); + Layer0 l0 = Layer0.getInstance(graph); + + Collection dependencies = graph.getObjects(r, sr.IsHeadOf); + + for(Resource d : dependencies) { + if(graph.isInstanceOf(d, sr.Dependency)) { + Resource tail = graph.getPossibleObject(d, sr.HasTail); + if(tail != null) { + Object name = graph.getPossibleRelatedValue(tail, l0.HasName); + if(name != null) + variables.add((String)name); + } + } + } + } + return variables; + } + + /** + * Is reference reachable from variable + * + * @param graph + * @param variable + * @param reference + * @return + * @throws DatabaseException + */ + public static boolean isReachable(ReadGraph graph, Resource variable, String reference) throws DatabaseException { + Layer0 l0 = Layer0.getInstance(graph); + SysdynResource sr = SysdynResource.getInstance(graph); + Resource configuration = graph.getSingleObject(variable, l0.PartOf); + String varName; + for(Resource var : graph.syncRequest(new ObjectsWithType(configuration, l0.ConsistsOf, sr.Variable))) { + varName = graph.getRelatedValue(var, l0.HasName, Bindings.STRING); + if(varName != null && reference.equals(varName)) + return true; + } + return false; + } +} -- 2.47.1