/******************************************************************************* * Copyright (c) 2012 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.modeling.userComponent; import java.util.Map; import org.simantics.databoard.Bindings; import org.simantics.databoard.Datatypes; import org.simantics.databoard.adapter.AdaptException; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.parser.repository.DataTypeSyntaxError; import org.simantics.databoard.parser.repository.DataValueRepository; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.NumberType; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Statement; import org.simantics.db.WriteGraph; import org.simantics.db.common.CommentMetadata; import org.simantics.db.common.request.UnaryRead; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.exception.ServiceException; import org.simantics.db.layer0.request.ModelInstances; import org.simantics.db.layer0.util.Layer0Utils; import org.simantics.layer0.Layer0; import org.simantics.modeling.ModelingResources; import org.simantics.modeling.NewSymbol; import org.simantics.operation.Layer0X; import org.simantics.scl.runtime.tuple.Tuple; import org.simantics.scl.runtime.tuple.Tuple3; import org.simantics.selectionview.SelectionViewResources; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.structural2.utils.StructuralUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import gnu.trove.map.hash.THashMap; public class ComponentTypeCommands { private static final Logger LOGGER = LoggerFactory.getLogger(ComponentTypeCommands.class); public static void applyCode(WriteGraph g, Resource componentType, String code) throws DatabaseException { StructuralResource2 STR = StructuralResource2.getInstance(g); g.claimLiteral(componentType, STR.ProceduralComponentType_code, code, Bindings.STRING); } public static Resource createConnectionPoint(WriteGraph g, Resource componentType, Resource cp) throws DatabaseException { return StructuralUtils.createConnectionPoint(g, componentType, cp); } public static Resource createMonitorPropertyWithDefaults(WriteGraph g, Resource componentType) throws DatabaseException { Layer0 L0 = Layer0.getInstance(g); StructuralResource2 STR = StructuralResource2.getInstance(g); ModelingResources MOD = ModelingResources.getInstance(g); Resource monitorType = g.getPossibleObject(componentType, STR.ComponentType_HasDefaultMonitorValueType); if(monitorType == null) monitorType = MOD.MonitorValue; Resource relation = createPropertyWithDefaultsBase(g, componentType, "newProperty"); g.claim(relation, L0.HasRange, monitorType); Resource assertion = g.newResource(); g.claim(componentType, L0.Asserts, assertion); g.claim(assertion, L0.InstanceOf, L0.Assertion); g.claim(assertion, L0.HasPredicate, relation); Resource value = g.newResource(); g.claim(value, L0.InstanceOf, monitorType); g.claimLiteral(value, L0.HasValueType, L0.String, "Double", Bindings.STRING); g.claimLiteral(value, L0.SCLValue_expression, L0.String, "", Bindings.STRING); g.claim(assertion, L0.HasObject, value); return relation; } public static Resource createPropertyWithDefaults(WriteGraph g, Resource componentType) throws DatabaseException { g.markUndoPoint(); Layer0 L0 = Layer0.getInstance(g); Resource relation = createPropertyWithDefaultsBase(g, componentType, "newProperty"); Resource assertion = g.newResource(); g.claim(componentType, L0.Asserts, assertion); g.claim(assertion, L0.InstanceOf, L0.Assertion); g.claim(assertion, L0.HasPredicate, relation); Resource value = g.newResource(); g.claim(value, L0.InstanceOf, L0.Literal); g.claimLiteral(value, L0.HasDataType, L0.DataType, Datatypes.DOUBLE, Bindings.getBindingUnchecked(Datatype.class)); g.claimLiteral(value, L0.HasValueType, L0.String, "Double", Bindings.STRING); g.claimValue(value, 0.0, Bindings.DOUBLE); g.claim(assertion, L0.HasObject, value); return relation; } public static Resource createPropertyWithDefaultsBase(WriteGraph g, Resource componentType, String defaultName) throws DatabaseException { Layer0 L0 = Layer0.getInstance(g); StructuralResource2 STR = StructuralResource2.getInstance(g); ModelingResources MOD = ModelingResources.getInstance(g); String name = NameUtils.findFreshEscapedName(g, defaultName, componentType); Resource relation = g.newResource(); g.claim(relation, L0.SubrelationOf, null, L0.HasProperty); boolean hadProperty = false; for(Resource type : g.getObjects(componentType, STR.ComponentType_HasDefaultPropertyRelationType)) { if(g.isInheritedFrom(type, STR.Property)) hadProperty = true; g.claim(relation, L0.InstanceOf, type); } if(!hadProperty) g.claim(relation, L0.InstanceOf, STR.Property); g.claimLiteral(relation, L0.HasName, name); g.claim(componentType, L0.ConsistsOf, L0.PartOf, relation); g.claim(relation, L0.HasDomain, L0.DomainOf, componentType); Resource invRelation = g.newResource(); g.claim(invRelation, L0.SubrelationOf, null, L0.PropertyOf); g.claim(relation, L0.ConsistsOf, L0.PartOf, invRelation); g.claimLiteral(invRelation, L0.HasName, "Inverse"); g.claim(relation, L0.InverseOf, invRelation); g.claimLiteral(relation, L0.RequiresValueType, "Double"); SelectionViewResources SEL = SelectionViewResources.getInstance(g); Resource category = g.getPossibleObject(relation, SEL.HasStandardPropertyInfo); if(category == null) { g.claim(relation, SEL.HasStandardPropertyInfo, MOD.UserDefinedPropertyInfo); } if(!g.isInstanceOf(relation, SEL.GenericParameterType)) g.claim(relation, L0.InstanceOf, SEL.GenericParameterType); CommentMetadata cm = g.getMetadata(CommentMetadata.class); g.addMetadata(cm.add("Created new property " + name + " for " + g.getRelatedValue2(componentType, L0.HasName, Bindings.STRING) + " " + componentType.toString())); return relation; } public static Resource createProperty(WriteGraph graph, Resource componentType, String name, String type, String unit, String range, String label, String description) throws DatabaseException { Resource property = createPropertyWithDefaults(graph, componentType); rename(graph, property, name); setRequiredType(graph, componentType, property, type); convertDefaultValue(graph, componentType, property, type); //setDefaultValue(graph, type, relation, valueText) if (!type.equals("String")) { setUnit(graph, componentType, property, unit); setRange(graph, componentType, property, range); } setLabel(graph, property, label); setDescription(graph, property, description); return property; } public static void removeProperty(WriteGraph g, Resource componentType, Resource property) throws DatabaseException { Layer0 L0 = Layer0.getInstance(g); for(Resource assertion : g.getObjects(property, L0.HasPredicateInverse)) g.deny(assertion); g.deny(property); String name = g.getPossibleRelatedValue2(componentType, L0.HasName); CommentMetadata cm = g.getMetadata(CommentMetadata.class); g.addMetadata(cm.add("Removed property " + property + " from component/annotation " + name + ", resource "+ componentType)); } public static void rename(WriteGraph g, Resource resource, String newName) throws DatabaseException { Layer0 L0 = Layer0.getInstance(g); String prevName = g.getPossibleRelatedValue2(resource, L0.HasName); g.claimLiteral(resource, L0.HasName, newName); CommentMetadata cm = g.getMetadata(CommentMetadata.class); g.addMetadata(cm.add("Renamed component/annotation type from " + prevName + " to " + newName + ", resource " + resource )); } public static void setRequiredType(WriteGraph g, Resource property, String requiredType) throws DatabaseException { setRequiredType(g, null, property, requiredType); } public static void setRequiredType(WriteGraph g, Resource componentType, Resource property, String requiredType) throws DatabaseException { Layer0 L0 = Layer0.getInstance(g); g.claimLiteral(property, L0.RequiresValueType, requiredType); if (componentType != null) { StructuralResource2 STR = StructuralResource2.getInstance(g); for (Resource assertedValue : g.getAssertedObjects(componentType, property)) { if (g.isInstanceOf(assertedValue, STR.MonitorValue)) { g.claimLiteral(assertedValue, L0.HasValueType, requiredType); } } } CommentMetadata cm = g.getMetadata(CommentMetadata.class); g.addMetadata(cm.add("Set required type "+ requiredType + " for component/annotation " + property)); } public static void editType(WriteGraph graph, Resource componentType, Resource property, boolean convertDefaultValue, String newValue) throws DatabaseException { ComponentTypeCommands.setRequiredType(graph, componentType, property, newValue); if (convertDefaultValue) { ComponentTypeCommands.convertDefaultValue(graph, componentType, property, newValue); Map instances = graph.sync(new ModelInstances(componentType, componentType)); for(Resource instance : instances.values()) { ComponentTypeCommands.convertInstantiatedValue(graph, instance, property, newValue); } } } static class AssertionMap extends UnaryRead> { public AssertionMap(Resource parameter) { super(parameter); } @Override public Map perform(ReadGraph graph) throws DatabaseException { THashMap result = new THashMap(); Layer0 L0 = Layer0.getInstance(graph); for(Resource assertion : graph.getObjects(parameter, L0.Asserts)) result.put(graph.getSingleObject(assertion, L0.HasPredicate), graph.getSingleObject(assertion, L0.HasObject)); return result; } } public static Resource getAssertedObject(ReadGraph g, Resource type, Resource relation) throws DatabaseException { return g.syncRequest(new AssertionMap(type)).get(relation); } public static void setMonitorExpression(WriteGraph g, Resource type, Resource relation, String valueText) throws DatabaseException { Resource object = getAssertedObject(g, type, relation); if(object == null) { LOGGER.warn("Didn't find assertion for " + NameUtils.getSafeName(g, relation) + " in " + NameUtils.getSafeName(g, type) + "."); return; } Layer0 L0 = Layer0.getInstance(g); g.claimLiteral(object, L0.SCLValue_expression, valueText, Bindings.STRING); } public static void setDefaultValue(WriteGraph g, Resource type, Resource relation, String valueText) throws DatabaseException { Resource object = getAssertedObject(g, type, relation); if(object == null) { LOGGER.warn("Didn't find assertion for " + NameUtils.getSafeName(g, relation) + " in " + NameUtils.getSafeName(g, type) + "."); return; } if(valueText.length() > 0 && valueText.charAt(0) == '=') { String expression = valueText.substring(1); Layer0 L0 = Layer0.getInstance(g); ModelingResources MOD = ModelingResources.getInstance(g); if(!g.isInstanceOf(object, MOD.SCLValue)) { Resource assertion = g.getSingleObject(object, L0.HasObjectInverse); g.deny(assertion, L0.HasObject, object); object = g.newResource(); g.claim(object, L0.InstanceOf, MOD.SCLValue); g.claim(assertion, L0.HasObject, object); } g.claimLiteral(object, L0.SCLValue_expression, L0.String, expression, Bindings.STRING); Layer0Utils.addCommentMetadata(g, "Modified " + g.getRelatedValue2(relation, Layer0.getInstance(g).HasName, Bindings.STRING) + " with new expression '" + expression + "'"); } else { Layer0 L0 = Layer0.getInstance(g); ModelingResources MOD = ModelingResources.getInstance(g); if(g.isInstanceOf(object, MOD.SCLValue)) { Resource assertion = g.getSingleObject(object, L0.HasObjectInverse); g.deny(assertion, L0.HasObject, object); object = g.newResource(); String sclType = g.getRelatedValue(relation, L0.RequiresValueType, Bindings.STRING); Datatype newDatatype = TypeConversion.convertSCLTypeToDatatype(sclType); g.claim(object, L0.InstanceOf, L0.Literal); Binding ntb = Bindings.getBindingUnchecked(Datatype.class); g.claimLiteral(object, L0.HasDataType, L0.DataType, newDatatype, ntb); g.claim(assertion, L0.HasObject, object); } Datatype dt = g.getDataType(object); Binding binding = Bindings.getBinding(dt); Object value; try { value = binding.parseValue(valueText, new DataValueRepository()); g.claimValue(object, value, binding); Layer0Utils.addCommentMetadata(g, "Modified " + g.getRelatedValue2(relation, Layer0.getInstance(g).HasName, Bindings.STRING) + " with new value " + value.toString()); } catch (DataTypeSyntaxError e) { e.printStackTrace(); } catch (BindingException e) { e.printStackTrace(); } } } /** * @param graph graph write transaction handle * @param type component type to edit * @param relation component type property relation to edit * @param unit null to remove unit description * @throws DatabaseException */ public static void setUnit(WriteGraph graph, Resource type, Resource relation, String unit) throws DatabaseException { Resource object = getAssertedObject(graph, type, relation); if (object == null) { LOGGER.warn("Didn't find assertion for " + NameUtils.getSafeName(graph, relation) + " in " + NameUtils.getSafeName(graph, type) + "."); return; } Layer0X L0X = Layer0X.getInstance(graph); boolean hasRequiresDataType = graph.hasStatement(relation, L0X.RequiresDataType); if (hasRequiresDataType) { Datatype dt = graph.getDataType(object); if (dt instanceof NumberType) { Layer0 L0 = Layer0.getInstance(graph); NumberType nt = (NumberType) Bindings.DATATYPE.cloneUnchecked(dt); nt.setUnit(unit); graph.claimLiteral(object, L0.HasDataType, L0.DataType, nt, Bindings.DATATYPE); graph.claimLiteral(relation, L0X.RequiresDataType, L0.DataType, nt, Bindings.DATATYPE); } } String oldUnit = graph.getPossibleRelatedValue2(relation, L0X.HasUnit, Bindings.STRING); graph.claimLiteral(relation, L0X.HasUnit, unit, Bindings.STRING); CommentMetadata cm = graph.getMetadata(CommentMetadata.class); graph.addMetadata(cm.add("Set unit from " + oldUnit + " to " + unit + " for component/annotation " + type + " property " + relation)); } /** * @param graph graph write transaction handle * @param type component type to modify * @param relation property relation of a component type * @param newRange new range definition or null to remove range restriction * @throws DatabaseException */ public static void setRange(WriteGraph graph, Resource type, Resource relation, String newRange) throws DatabaseException { Resource object = getAssertedObject(graph, type, relation); if (object == null) { LOGGER.warn("Didn't find assertion for " + NameUtils.getSafeName(graph, relation) + " in " + NameUtils.getSafeName(graph, type) + "."); return; } Datatype dt = graph.getDataType(object); if (dt instanceof NumberType) { NumberType nt = (NumberType) dt; Binding ntb = Bindings.getBindingUnchecked(Datatype.class); nt.setRange(newRange); Layer0 L0 = Layer0.getInstance(graph); Layer0X L0X = Layer0X.getInstance(graph); graph.claimLiteral(object, L0.HasDataType, L0.DataType, nt, ntb); graph.claimLiteral(relation, L0X.RequiresDataType, L0.DataType, nt, ntb); CommentMetadata cm = graph.getMetadata(CommentMetadata.class); graph.addMetadata(cm.add("Setted range " + newRange + " for component/annotation " + type)); } } public static Tuple getDatatypeValueAndBinding(ReadGraph g, Resource object, String newSCLType) throws DatabaseException { Datatype newDatatype = TypeConversion.convertSCLTypeToDatatype(newSCLType); if(newDatatype == null) { LOGGER.warn("Couldn't convert default value to <" + newSCLType + ">."); return null; } Binding newBinding = Bindings.getBinding(newDatatype); Datatype oldDatatype = g.getDataType(object); Binding oldBinding = Bindings.getBinding(oldDatatype); Object oldValue = g.getValue(object, oldBinding); Object newValue; try { newValue = Bindings.adapt(oldValue, oldBinding, newBinding); } catch (AdaptException e) { try { newValue = newBinding.createDefault(); } catch (BindingException e1) { e1.printStackTrace(); return null; } } return new Tuple3(newDatatype, newValue, newBinding); } public static void convertDefaultValue(WriteGraph g, Resource type, Resource relation, String newSCLType) throws DatabaseException { Resource object = getAssertedObject(g, type, relation); if(object == null) { LOGGER.warn("Didn't find assertion for " + NameUtils.getSafeName(g, relation) + " in " + NameUtils.getSafeName(g, type) + "."); return; } Tuple tuple = getDatatypeValueAndBinding(g, object, newSCLType); if (tuple == null) return; Layer0 L0 = Layer0.getInstance(g); g.claimLiteral(object, L0.HasDataType, L0.DataType, tuple.get(0), Bindings.getBindingUnchecked(Datatype.class)); g.claimLiteral(object, L0.HasValueType, g.getRelatedValue(relation, L0.RequiresValueType, Bindings.STRING), Bindings.STRING); g.claimValue(object, tuple.get(1), (Binding)tuple.get(2)); } public static void convertInstantiatedValue(WriteGraph g, Resource instance, Resource relation, String newSCLType) throws DatabaseException { Statement stm = g.getPossibleStatement(instance, relation); if(stm != null && !stm.isAsserted(instance)) { Resource object = stm.getObject(); // We can only convert literals Layer0 L0 = Layer0.getInstance(g); if(!g.isInstanceOf(object, L0.Literal)) return; Tuple tuple = getDatatypeValueAndBinding(g, object, newSCLType); g.claimLiteral(object, L0.HasDataType, L0.DataType, tuple.get(0), Bindings.getBindingUnchecked(Datatype.class)); g.claimLiteral(object, L0.HasValueType, g.getRelatedValue(relation, L0.RequiresValueType, Bindings.STRING), Bindings.STRING); g.claimValue(object, tuple.get(1), (Binding)tuple.get(2)); } } /** * @param graph graph write transaction handle * @param relation component type property relation to edit * @param newDescription new label or null to remove label * @throws DatabaseException */ public static void setLabel(WriteGraph graph, Resource relation, String newLabel) throws DatabaseException { setProperty(graph, relation, Layer0.getInstance(graph).HasLabel, newLabel); CommentMetadata cm = graph.getMetadata(CommentMetadata.class); graph.addMetadata(cm.add("Setted label " + newLabel + " for component/annotation " + relation)); } /** * @param graph graph write transaction handle * @param relation component type property relation to edit * @param newDescription new description or null if new description * @throws DatabaseException */ public static void setDescription(WriteGraph graph, Resource relation, String newDescription) throws DatabaseException { setProperty(graph, relation, Layer0.getInstance(graph).HasDescription, newDescription); CommentMetadata cm = graph.getMetadata(CommentMetadata.class); graph.addMetadata(cm.add("Setted description " + newDescription + " for component/annotation " + relation)); } /** * @param graph graph write transaction handle * @param relation component type property relation to edit * @param newValue new property value or null to remove property * @throws DatabaseException */ public static void setProperty(WriteGraph graph, Resource relation, Resource property, String newValue) throws DatabaseException { if (newValue != null) { graph.claimLiteral(relation, property, newValue, Bindings.STRING); } else { graph.denyValue(relation, property); } } /** * @param graph * @param componentType * @return the created symbol */ public static Resource createSymbol(WriteGraph graph, Resource componentType) throws DatabaseException { return NewSymbol.createSymbol(graph, componentType); } /** * Converts to a camelCase name to a more user-readable * Camel Case label. * *

* Examples *

     * "fooBarBazBAR" => "Foo Bar Baz BAR"
     * " fooBarBazBAR" => " Foo Bar Baz BAR"
     * "_fooBarBazBAR" => "_Foo Bar Baz BAR"
     * "_FooBarBazBAR" => "_Foo Bar Baz BAR"
     * " _ fooBarBazBAR" => " _ Foo Bar Baz BAR"
     * 
* * @param str camelCase SCL identifier name * @return labelified Camel Case string */ public static String camelCaseNameToLabel(String str) { int len = str.length(); StringBuilder sb = new StringBuilder(len*2); boolean wasLastUpper = false; boolean isFirstEncounteredLetter = true; for (int i = 0; i < len; ++i) { char ch = str.charAt(i); boolean space = Character.isWhitespace(ch); if (space) { sb.append(ch); continue; } boolean isUpperCaseLetter = Character.isUpperCase(ch); boolean isLetterOrDigit = Character.isLetterOrDigit(ch); if (!isFirstEncounteredLetter && isUpperCaseLetter && !wasLastUpper && isLetterOrDigit) { sb.append(' '); sb.append(ch); } else { if (isLetterOrDigit && isFirstEncounteredLetter) sb.append(Character.toUpperCase(ch)); else sb.append(ch); if (isFirstEncounteredLetter) isFirstEncounteredLetter = !isLetterOrDigit; } wasLastUpper = isUpperCaseLetter; } return sb.toString(); } public static void saveProceduralCodeWithUC(WriteGraph graph, Resource componentType, String newText) throws DatabaseException { StructuralResource2 STR = StructuralResource2.getInstance(graph); Resource code = graph.getPossibleObject(componentType, STR.ProceduralComponentType_code); saveProceduralCode(graph, code, newText); } public static void saveProceduralCode(WriteGraph graph, Resource resource, String newText) throws ServiceException { graph.claimValue(resource, newText, Bindings.STRING); Layer0Utils.addCommentMetadata(graph, "Saved Procedural Component Type SCL Code"); } }