/******************************************************************************* * Copyright (c) 2007, 2010 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.db.layer0.variable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.mutable.Variant; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.NumberType; import org.simantics.databoard.util.URIStringUtils; import org.simantics.db.ReadGraph; import org.simantics.db.RequestProcessor; import org.simantics.db.Resource; import org.simantics.db.common.request.PossibleIndexRoot; import org.simantics.db.common.request.TernaryRead; import org.simantics.db.common.utils.Logger; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.exception.MissingVariableException; import org.simantics.db.layer0.request.Model; import org.simantics.db.layer0.request.PossibleActiveVariableFromVariable; import org.simantics.db.layer0.request.PossibleVariableIndexRoot; import org.simantics.db.layer0.request.PossibleVariableModel; import org.simantics.db.layer0.request.PropertyInfo; import org.simantics.db.layer0.request.PropertyInfoRequest; import org.simantics.db.layer0.request.ResourceURIToVariable; import org.simantics.db.layer0.request.VariableIndexRoot; import org.simantics.db.layer0.request.VariableURI; import org.simantics.layer0.Layer0; import org.simantics.operation.Layer0X; import org.simantics.project.ontology.ProjectResource; import org.simantics.scl.runtime.function.Function1; import org.simantics.scl.runtime.function.Function2; import org.simantics.scl.runtime.function.Function3; import org.simantics.simulation.ontology.SimulationResource; import org.simantics.simulator.variable.exceptions.NodeManagerException; import org.simantics.utils.datastructures.Pair; import gnu.trove.map.hash.TObjectIntHashMap; final public class Variables { public static final Variant PENDING_NODE_VALUE = new Variant(); public static final NodeStructure PENDING_NODE_STRUCTURE = new NodeStructure(Collections.emptyMap(), Collections.emptyMap()) { public boolean equals(Object object) { return this == object; } }; public static enum Role { CHILD("/"), PROPERTY("#"); transient final String identifier; private Role(String identifier) { this.identifier = identifier; } final public String getIdentifier() { return identifier; } public static Role getRole( String identifier ) { for (Role role : Role.values()) if (role.identifier.equals( identifier )) return role; return null; } } // use getPredicate //final public static String PREDICATE = "PREDICATE"; @Deprecated // use getName final public static String NAME = "HasName"; final public static String CLASSIFICATIONS = "classifications"; final public static String EXPRESSION = "HasExpression"; final public static String INPUT_VALIDATOR = "HasInputValidator"; final public static String INPUT_MODIFIER = "HasInputModifier"; final public static String FORMATTER = "HasFormatter"; final public static String STANDARD_RESOURCE = "hasStandardResource"; //@Deprecated // use getPresents //final public static String REPRESENTS = "Represents"; final public static String TYPE = "Type"; final public static String URI = "URI"; /** * @deprecated use {@link Variable#getRVI(ReadGraph)} and {@link RVI} instead. */ // @Deprecated // final public static String SERIALISED = "Serialised"; // @Deprecated // use getParent //final public static String PARENT = "Parent"; //final public static String ROLE = "Role"; //final public static String DATATYPE = "DATATYPE"; //final public static String UNIT = "UNIT"; final public static String VALID = "valid"; final public static String REQUIRED = "required"; final public static String DEFAULT = "default"; final public static String READONLY = "readOnly"; final public static String VALIDATOR = "validator"; final public static String LABEL = "HasLabel"; final public static String ENUMERATION_VALUES = "HasEnumerationValues"; final public static String CUSTOM_MODIFIER = "HasCustomModifier"; final public static String DISPLAY_COLUMN = "HasDisplayColumn"; final public static String DISPLAY_PROPERTY = "HasDisplayProperty"; final public static String DISPLAY_VALUE = "HasDisplayValue"; final public static String DISPLAY_UNIT = "HasDisplayUnit"; final public static String CONVERTED_VALUE = "convertedValue"; /** * This property should exist for array valued property variables. */ final public static String ARRAY_SIZE = "ARRAY_SIZE"; @Deprecated // use etc. variable.adapt(graph, Interface.class).getResource() final public static String RESOURCE = "Resource"; @Deprecated final public static String CONTAINER_RESOURCE = "ContainerResource"; @Deprecated final public static String PROPERTY_RESOURCE = "PROPERTY_RESOURCE"; @Deprecated public final static String[] builtins = { TYPE, RESOURCE, URI //, SERIALISED }; public static Variable getPossibleVariable(ReadGraph graph, Resource resource) throws DatabaseException { String uri = graph.getPossibleURI(resource); return uri != null ? getPossibleVariable(graph, uri) : null; } public static Variable getPossibleVariable(ReadGraph graph, String uri) throws DatabaseException { try { return getVariable(graph, uri); } catch (DatabaseException e) { return null; } } public static Variable getVariable(ReadGraph graph, Resource resource) throws DatabaseException { return getVariable(graph, graph.getURI(resource)); } public static Variable getVariable(ReadGraph graph, String uri) throws DatabaseException { try { return graph.sync(new ResourceURIToVariable(uri)); } catch (MissingVariableException e) { return VariableRepository.get(graph, uri); } } private static int commonPrefixLength(String a, String b) { int maxC = Math.min(a.length(), b.length()); for(int c=0;c 0 && !isSplitPos(baseURI, prefixLength);--prefixLength); } if(prefixLength == baseURI.length()) return otherURI.substring(prefixLength); else return prefixByParentPath( pathLength(baseURI.substring(prefixLength)), otherURI.substring(prefixLength)); } public static String getRVI2(ReadGraph graph, Variable base, Variable other) throws DatabaseException { TObjectIntHashMap baseLength = new TObjectIntHashMap(); for(int depth=0;base != null;base = base.getParent(graph),++depth) baseLength.put(base, depth); Variable cur; for(cur=other;!baseLength.containsKey(cur);cur=cur.getParent(graph)); // To string String curURI = cur.getURI(graph); String otherURI = other.getURI(graph); return prefixByParentPath(baseLength.get(cur), otherURI.substring(curURI.length())); } public static String getProjectRVI(ReadGraph graph, Variable variable) throws DatabaseException { Resource project = getProject(graph, variable); String projectURI = graph.getURI(project); return variable.getURI(graph).substring(projectURI.length()); } private static int getSegmentEnd(String suffix) { int pos; for(pos=1;pos getPath(ReadGraph graph, Variable base, Variable var) throws DatabaseException { if(!isChild(graph, base, var)) return null; LinkedList result = new LinkedList(); var = var.getParent(graph); while(!var.equals(base)) { result.addFirst(var); var = var.getParent(graph); } return result; } public static Variable getChild(ReadGraph graph, Variable base, Variable var) throws DatabaseException { List path = getPath(graph, base, var); if(path == null || path.size() == 0) return null; return path.get(0); } public static boolean isChild(ReadGraph graph, Variable base, Variable var) throws DatabaseException { if(base.equals(var)) return false; return var.getURI(graph).startsWith(base.getURI(graph)); } public static Variable switchRealization(ReadGraph graph, Variable variable, Resource realization) throws DatabaseException { Resource current = getRealization(graph, variable); if(current == null) throw new DatabaseException("No current realization found for variable"); return switchRealization(graph, variable, current, realization); } public static Variable switchPossibleContext(ReadGraph graph, Variable variable, Resource realization) throws DatabaseException { Variable current = getPossibleContext(graph, variable); if (current == null) return null; Resource currentContext = current.getPossibleRepresents(graph); if (currentContext == null) return null; return switchPossibleRealization(graph, variable, currentContext, realization); } public static Variable switchRealization(ReadGraph graph, Variable variable, Variable realization) throws DatabaseException { Resource current = getRealization(graph, variable); return switchRealization(graph, variable, current, realization); } public static Variable switchRealization(ReadGraph graph, Variable variable, Resource currentRealization, Resource targetRealization) throws DatabaseException { String currentURI = graph.getURI(currentRealization); String targetURI = graph.getURI(targetRealization); String variableURI = variable.getURI(graph); String targetVariableURI = targetURI + variableURI.substring(currentURI.length()); return getVariable(graph, targetVariableURI); } public static Variable switchRealization(ReadGraph graph, Variable variable, Resource currentRealization, Variable targetRealization) throws DatabaseException { String currentURI = graph.getURI(currentRealization); String targetURI = targetRealization.getURI(graph); String variableURI = variable.getURI(graph); String targetVariableURI = targetURI + variableURI.substring(currentURI.length()); return getVariable(graph, targetVariableURI); } public static Variable switchPossibleRealization(ReadGraph graph, Variable variable, Resource currentRealization, Resource targetRealization) throws DatabaseException { String currentURI = graph.getURI(currentRealization); String targetURI = graph.getURI(targetRealization); String variableURI = variable.getURI(graph); String targetVariableURI = targetURI + variableURI.substring(currentURI.length()); return getPossibleVariable(graph, targetVariableURI); } public static Variable toConfigurationVariable(ReadGraph graph, Variable variable) throws DatabaseException { Variable config = getConfigurationContext(graph, variable); return switchRealization(graph, variable, config); } public static Variable toPossibleConfigurationVariable(ReadGraph graph, Variable variable) throws DatabaseException { Resource represents = variable.getPossibleRepresents(graph); if(represents == null) return null; Resource config = getPossibleConfigurationContextResource(graph, represents); if(config == null) return null; return switchPossibleContext(graph, variable, config); } public static String toRVI(ReadGraph graph, List compositePath) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); StringBuilder rvi = new StringBuilder(); for (Resource composite : compositePath) { String name = graph.getPossibleRelatedValue(composite, L0.HasName); if (name == null) return null; rvi.append('/'); String escapedName = URIStringUtils.escape(name); rvi.append(escapedName); } return rvi.toString(); } public static String appendRVI(ReadGraph graph, String modelURI, String rvi, Resource configuration) throws DatabaseException { Layer0 L0 = Layer0.getInstance(graph); String partName = graph.getPossibleRelatedValue(configuration, L0.HasName); if(partName == null) throw new MissingVariableException("Can not append a child corresponding to " + configuration + " to rvi '" + rvi + "' since there is no name."); String escaped = URIStringUtils.escape(partName); return rvi + "/" + escaped; } public static boolean isValid(ReadGraph graph, Variable variable) { if(variable == null) return false; try { variable.getURI(graph); } catch (DatabaseException e) { return false; } return true; } public static Variable possibleChildWithType(ReadGraph graph, Variable variable, Resource targetType) throws DatabaseException { Variable found = null; for(Variable child : variable.getChildren(graph)) { Resource type = child.getPossiblePropertyValue(graph, Variables.TYPE); if(type != null && graph.isInheritedFrom(type, targetType)) { if(found != null) return null; found = child; } } return found; } public static Collection childrenWithType(ReadGraph graph, Variable variable, Resource targetType) throws DatabaseException { ArrayList result = new ArrayList(); for(Variable child : variable.getChildren(graph)) { Resource type = child.getPossiblePropertyValue(graph, Variables.TYPE); if(graph.isInheritedFrom(type, targetType)) result.add(child); } return result; } public static T adapt(ReadGraph graph, Variable variable, String property, Class clazz) throws DatabaseException { Resource resource = variable.getPropertyValue(graph, property); return graph.adapt(resource, clazz); } public static T getPossiblePropertyValue(RequestProcessor processor, Variable variable, Resource property, Binding binding) { try { return processor.sync(new TernaryRead(variable, property, binding) { @Override public T perform(ReadGraph graph) throws DatabaseException { return parameter.getPossiblePropertyValue(graph, parameter2, parameter3); } }); } catch (DatabaseException e) { return null; } } public static Variable possibleActiveVariable(ReadGraph graph, Variable variable) throws DatabaseException { Variable activeVariable = graph.sync(new PossibleActiveVariableFromVariable(variable)); return activeVariable; } public static Variant requestNodeValue(ReadGraph graph, VariableNode node) throws DatabaseException { return requestNodeValue(graph, node, null); } public static Variant requestNodeValue(ReadGraph graph, VariableNode node, final Binding binding) throws DatabaseException { Variant value = graph.syncRequest(new NodeValueRequest(node, binding)); if(PENDING_NODE_VALUE == value && graph.getSynchronous()) { // In this case a PENDING value was previously cached but now the value needs to be obtained for real. ValueGetter getter = new ValueGetter(node, binding); try { node.support.manager.getRealm().syncExec(getter); } catch (InterruptedException e) { Logger.defaultLogError(e); } if (getter.exception != null) throw new DatabaseException(getter.exception); return getter.result; } return value; } public static class NodeStructure { // Immutable but wrapped with Collections.unmodifiableMap as an optimization public final Map children; // Immutable but not wrapped with Collections.unmodifiableMap as an optimization public final Map properties; private final int hash; public NodeStructure(Map children, Map properties) { this.children = children; this.properties = properties; this.hash = calcHash(); } private int calcHash() { return 31*children.hashCode() + 41*properties.hashCode(); } @Override public int hashCode() { return hash; } @Override public boolean equals(Object object) { if (this == object) return true; else if (object == null || object == Variables.PENDING_NODE_STRUCTURE) return false; else if (!(object instanceof NodeStructure)) return false; NodeStructure r = (NodeStructure)object; return r.children.equals(children) && r.properties.equals(properties); } } public static NodeStructure requestNodeStructure(ReadGraph graph, VariableNode node) throws DatabaseException { NodeStructure value = graph.syncRequest(new NodeStructureRequest(node)); if (value == null) throw new DatabaseException("External data access error"); if(PENDING_NODE_STRUCTURE == value && graph.getSynchronous()) { // In this case a PENDING value was previously cached but now the value needs to be obtained for real. StructureGetter getter = new StructureGetter(node); try { node.support.manager.getRealm().syncExec(getter); } catch (InterruptedException e) { Logger.defaultLogError(e); throw new DatabaseException("External data access error", e); } if (getter.exception != null) throw new DatabaseException("External data access error", getter.exception); if (getter.result == null) throw new DatabaseException("External data access error"); return getter.result; } return value; } public static String getPossibleUnit(ReadGraph graph, Variable variable) throws DatabaseException { try { Resource predicate = variable.getPossiblePredicateResource(graph); if(predicate != null) { PropertyInfo info = graph.syncRequest(new PropertyInfoRequest(predicate)); if(info.definedUnit != null) return info.definedUnit; } Variant variant = variable.getVariantValue(graph); Binding binding = variant.getBinding(); if(binding == null) return null; Datatype dt = binding.type(); if(!(dt instanceof NumberType)) return null; NumberType nt = (NumberType)dt; return nt.getUnit(); } catch (DatabaseException e) { return null; } } /** * @param graph * @param rvi * @param context1 primary context to use for resolving the specified RVI, must not be null * @param context2 secondary context to use for resolving the specified RVI, may be null * @return pair of variables where first is the resolved variable and second * is the context it was resolved with * @throws DatabaseException * @since 1.18.1 */ public static Pair resolvePossible(ReadGraph graph, RVI rvi, Variable context1, Variable context2) throws DatabaseException { Variable v = rvi.resolvePossible(graph, context1); if (v != null) return new Pair<>(v, context1); if (context2 != null) { v = rvi.resolvePossible(graph, context2); if (v != null) return new Pair<>(v, context2); } return null; } @SuppressWarnings("rawtypes") private static class ValueGetter implements VariableNodeReadRunnable { final VariableNode n; final Binding binding; Variant result; Exception exception; public ValueGetter(VariableNode n, Binding binding) { this.n = n; this.binding = binding; } @SuppressWarnings("unchecked") @Override public void run() { try { if (binding != null) result = new Variant(binding, n.support.manager.getValue(n.node, binding)); else result = n.support.manager.getValue(n.node); } catch (Exception e) { Logger.defaultLogError(e); exception = e; } } } @SuppressWarnings("rawtypes") private static class StructureGetter implements VariableNodeReadRunnable { final VariableNode n; NodeStructure result; Exception exception; public StructureGetter(VariableNode n) { this.n = n; } @Override public void run() { try { result = NodeStructureRequest.get(n); } catch (NodeManagerException e) { Logger.defaultLogError(e); exception = e; } } }; public static Variable tryGetProperty(ReadGraph graph, Resource entity, Resource property) throws DatabaseException { Variable v = Variables.getPossibleVariable(graph, entity); return v != null ? v.getPossibleProperty(graph, property) : null; } public static ValueAccessor createValueAccessor(Function1 getValue1, Function2 getValue2, Function2 setValue2, Function3 setValue3, Function1 getDatatype) { return new SCLValueAccessor(getValue1, getValue2, setValue2, setValue3, getDatatype); } }