package org.simantics.modeling.utils; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.simantics.databoard.Bindings; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.NumberType; import org.simantics.db.ReadGraph; import org.simantics.db.RequestProcessor; import org.simantics.db.Resource; import org.simantics.db.common.NamedResource; import org.simantics.db.common.procedure.adapter.TransientCacheAsyncListener; import org.simantics.db.common.request.IsEnumeratedValue; import org.simantics.db.common.request.UniqueRead; import org.simantics.db.common.utils.CommonDBUtils; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.request.PropertyInfo; import org.simantics.db.layer0.request.PropertyInfoRequest; import org.simantics.db.layer0.util.Layer0Utils; import org.simantics.db.layer0.variable.StandardGraphChildVariable; import org.simantics.db.layer0.variable.StandardGraphPropertyVariable; import org.simantics.db.layer0.variable.Variable; import org.simantics.layer0.Layer0; import org.simantics.modeling.scl.CompileProceduralSCLMonitorRequest; import org.simantics.modeling.scl.CompileSCLMonitorRequest; import org.simantics.operation.Layer0X; import org.simantics.scl.runtime.SCLContext; import org.simantics.scl.runtime.function.Function1; import org.simantics.structural.stubs.StructuralResource2; import org.simantics.utils.ui.ErrorLogger; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class HeadlessComponentTypePropertiesResultRequest extends UniqueRead { private static final Logger LOGGER = LoggerFactory.getLogger(HeadlessComponentTypePropertiesResultRequest.class); protected final Resource componentType; /** * @param componentTypeViewer */ public HeadlessComponentTypePropertiesResultRequest(Resource componentType) { this.componentType = componentType; } protected void readSectionSpecificData(ReadGraph graph, ComponentTypeViewerPropertyInfo info) throws DatabaseException { } public static ComponentTypeViewerPropertyInfo readPropertyInfo(ReadGraph graph, Resource relation, Resource componentType, boolean typeIsImmutable) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); Layer0X L0X = Layer0X.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); String name = graph.getRelatedValue(relation, L0.HasName); String type = graph.getPossibleRelatedValue(relation, L0.RequiresValueType); String label = graph.getPossibleRelatedValue(relation, L0.HasLabel); if (label == null) label = ""; //$NON-NLS-1$ String description = graph.getPossibleRelatedValue(relation, L0.HasDescription); if (description == null) description = ""; //$NON-NLS-1$ NumberType numberType = null; if(type == null) type = "Double"; //$NON-NLS-1$ String unit = graph.getPossibleRelatedValue(relation, L0X.HasUnit, Bindings.STRING); String defaultValue = "0"; //$NON-NLS-1$ String expression = null; if(componentType != null) { for(Resource assertion : graph.getAssertedObjects(componentType, relation)) { try { expression = graph.getPossibleRelatedValue(assertion, L0.SCLValue_expression, Bindings.STRING); if(expression != null) { defaultValue = "=" + expression; //$NON-NLS-1$ } else if (graph.sync(new IsEnumeratedValue(assertion))) { defaultValue = CommonDBUtils.getEnumerationValueName(graph, assertion); } else { Datatype dt = getPossibleDatatype(graph, assertion); if (dt == null) continue; if (dt instanceof NumberType) numberType = (NumberType) dt; Binding binding = Bindings.getBinding(dt); Object value = graph.getValue(assertion, binding); try { defaultValue = binding.toString(value, true); } catch (BindingException e) { ErrorLogger.defaultLogError(e); } } } catch(DatabaseException e) { ErrorLogger.defaultLogError(e); } } } String valid = expression != null ? validateMonitorExpression(graph, componentType, relation, expression) : null; boolean immutable = typeIsImmutable || graph.isImmutable(relation); ComponentTypeViewerPropertyInfo info = new ComponentTypeViewerPropertyInfo(relation, name, type, defaultValue, numberType, unit, label, description, expression, valid, immutable); return info; } @Override public ComponentTypePropertiesResult perform(ReadGraph graph) throws DatabaseException { List result = new ArrayList<>(); List connectionPoints = new ArrayList<>(); Layer0 L0 = Layer0.getInstance(graph); StructuralResource2 STR = StructuralResource2.getInstance(graph); boolean typeIsImmutable = graph.isImmutable(componentType) || graph.hasStatement(componentType, STR.ComponentType_Locked) || Layer0Utils.isPublished(graph, componentType) || Layer0Utils.isContainerPublished(graph, componentType); for(Resource relation : graph.getObjects(componentType, L0.DomainOf)) { if(graph.isSubrelationOf(relation, L0.HasProperty)) { ComponentTypeViewerPropertyInfo info = readPropertyInfo(graph, relation, componentType, typeIsImmutable); readSectionSpecificData(graph, info); result.add(info); } else if (graph.isInstanceOf(relation, STR.ConnectionRelation)) { NamedResource nr = new NamedResource(NameUtils.getSafeName(graph, relation), relation); connectionPoints.add(nr); } } Collections.sort(result); return new ComponentTypePropertiesResult(result, connectionPoints, typeIsImmutable); } private static Datatype getPossibleDatatype(ReadGraph graph, Resource literal) throws DatabaseException { Binding binding = Bindings.getBindingUnchecked(Datatype.class); for (Resource dataTypeResource : graph.getObjects(literal, Layer0.getInstance(graph).HasDataType)) { Datatype dt = graph.getPossibleValue(dataTypeResource, binding); if (dt != null) return dt; } return null; } public static String validateMonitorExpression(final RequestProcessor processor, final Resource componentType, final Resource relation, final String expression) { try { return processor.sync(new UniqueRead() { @Override public String perform(ReadGraph graph) throws DatabaseException { StructuralResource2 STR = StructuralResource2.getInstance(graph); // TODO: this is a bit hackish but should get the job done in most parts and // importantly indicates something for the user PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(relation), TransientCacheAsyncListener.instance()); Variable parent = new StandardGraphChildVariable(null, null, componentType) { public Resource getType(ReadGraph graph) throws DatabaseException { return componentType; }; public Variable getPossibleProperty(ReadGraph graph, String name) throws DatabaseException { Variable prop = super.getPossibleProperty(graph, name); if (prop != null) { return prop; } else { return getChild(graph, name); } }; }; for(Resource literal : graph.getAssertedObjects(componentType, relation)) { try { Variable context = new StandardGraphPropertyVariable(parent, null, null, info, literal); if(graph.isInstanceOf(componentType, STR.ProceduralComponentType)) { CompileProceduralSCLMonitorRequest.compileAndEvaluate(graph, context); } else { compileAndEvaluate(graph, context, expression); } } catch (Exception e) { String msg = e.getMessage(); int index = msg.indexOf(":"); //$NON-NLS-1$ if(index > 0) msg = msg.substring(index); return msg; } } return null; } }); } catch (DatabaseException e) { LOGGER.error("Could not validate ", e); } return null; } public static void compileAndEvaluate(ReadGraph graph, Variable context, String expression) throws DatabaseException { SCLContext sclContext = SCLContext.getCurrent(); Object oldGraph = sclContext.get("graph"); try { CompileSCLMonitorRequest compileSCLMonitorRequest = new ValidationCompilationRequest(graph, context, expression); Function1 exp = graph.syncRequest(compileSCLMonitorRequest); sclContext.put("graph", graph); //return exp.apply(context.getParent(graph)); } catch (DatabaseException e) { throw (DatabaseException)e; } catch (Throwable t) { throw new DatabaseException(t); } finally { sclContext.put("graph", oldGraph); } } private static final class ValidationCompilationRequest extends CompileSCLMonitorRequest { private final String expression; private ValidationCompilationRequest(ReadGraph graph, Variable context, String expression) throws DatabaseException { super(graph, context); this.expression = expression; } @Override protected String getExpressionText(ReadGraph graph) throws DatabaseException { return expression; } @Override public int hashCode() { return super.hashCode() + 37 * expression.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj) && ((ValidationCompilationRequest)obj).expression.equals(expression); } } }