/******************************************************************************* * 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.browsing.ui.graph.impl; import org.simantics.browsing.ui.content.Labeler.Modifier; import org.simantics.databoard.Bindings; import org.simantics.databoard.Datatypes; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.NumberBinding; import org.simantics.databoard.binding.StringBinding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.mutable.MutableStringBinding; import org.simantics.databoard.parser.repository.DataTypeSyntaxError; import org.simantics.databoard.parser.repository.DataValueRepository; import org.simantics.databoard.primitives.MutableString; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.NumberType; import org.simantics.databoard.units.IUnitConverter; import org.simantics.databoard.util.Range; import org.simantics.db.ReadGraph; import org.simantics.db.RequestProcessor; import org.simantics.db.Resource; import org.simantics.db.Session; import org.simantics.db.UndoContext; import org.simantics.db.WriteGraph; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.common.request.WriteRequest; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.util.PrimitiveValueParser; import org.simantics.db.layer0.variable.InputValidator; import org.simantics.db.layer0.variable.InputValidatorFactory; import org.simantics.db.layer0.variable.Variable; import org.simantics.db.layer0.variable.VariableWrite; import org.simantics.db.layer0.variable.Variables; import org.simantics.scl.runtime.function.Function1; import org.simantics.utils.datastructures.Callback; import org.simantics.utils.format.FormattingUtils; import org.simantics.utils.ui.ErrorLogger; /** * @author Tuukka Lehtonen */ public class VariableModifier implements Modifier { final protected Session session; final protected UndoContext undoContext; final protected Variable variable; final protected String targetUnit; private String initialValue; protected Throwable modifierFailed; private Datatype datatype; protected Binding binding; protected InputValidator variableInputValidator; public VariableModifier(RequestProcessor processor, Variable variable) { this(processor, variable, null); } public VariableModifier(RequestProcessor processor, Variable variable, String targetUnit) { this.variable = variable; this.session = processor.getSession(); this.undoContext = null; this.targetUnit = targetUnit; initializeModifier(processor); } public VariableModifier(RequestProcessor processor, UndoContext undoContext, Variable variable, String targetUnit) { this.variable = variable; this.undoContext = undoContext; this.session = processor.getSession(); this.targetUnit = targetUnit; initializeModifier(processor); } protected void initializeModifier(RequestProcessor processor) { try { processor.syncRequest(new ReadRequest() { @Override public void run(ReadGraph graph) throws DatabaseException { String expression = variable.getPossiblePropertyValue(graph, Variables.EXPRESSION); if(expression == null) { datatype = variable.getPossibleDatatype(graph); if (datatype == null) { // Deprecated. // This is here for some backwards compatibility. datatype = variable.getInterface(graph, Datatype.class); if (datatype == null) throw new org.simantics.db.exception.BindingException("no datatype for variable " + variable, null); } initialValue = getInitialValue(graph, datatype); binding = Bindings.getBinding(datatype); initializeValidator(graph, variable); } else { initialValue = "= " + expression; binding = Bindings.STRING; datatype = Datatypes.STRING; } } }); } catch (DatabaseException e) { ErrorLogger.defaultLogError("Modifier initialization failed, see exception for details.", e); modifierFailed = e; } } protected void initializeValidator(ReadGraph graph, Variable var) throws DatabaseException { //System.out.println(var.getURI(graph)); Resource resource = var.getPossibleRepresents(graph); // if (resource != null) // System.out.println("represents: " + graph.getPossibleURI(resource)); Resource pr = variable.getPossiblePredicateResource(graph); // Variable predicate = variable.getPossiblePropertyValue(graph, Variables.PREDICATE); // if (predicate != null) { // Resource pr = predicate.getPropertyValue(graph, Variables.REPRESENTS); if (pr != null) variableInputValidator = tryInputValidatorFactory(graph, var, resource, pr); // } if (variableInputValidator == null) { final Function1 validator = variable.getPossiblePropertyValue(graph, Variables.INPUT_VALIDATOR); if (validator != null) variableInputValidator = new InputValidator() { @Override public String isValid(Object newValue) { return validator.apply((String)newValue); } }; } if (variableInputValidator == null) { Variable parent = variable.getParent(graph); if (parent != null) { Resource parentResource = parent.getPossibleRepresents(graph); if (parentResource != null) variableInputValidator = tryInputValidatorFactory(graph, var, resource, parentResource); } } if (variableInputValidator == null) variableInputValidator = var.getInterface(graph, InputValidator.class); } private InputValidator tryInputValidatorFactory(ReadGraph graph, Variable var, Resource varRepresents, Resource r) throws DatabaseException { //System.out.println("tryInputValidatorFactory(" + var.getURI(graph) + ", " + NameUtils.getSafeName(graph, varRepresents) + ", " + NameUtils.getSafeName(graph, r) + ")"); InputValidatorFactory validatorFactory = graph.getPossibleAdapter(r, InputValidatorFactory.class); if (validatorFactory == null) return null; //System.out.println("got validator factory: " + validatorFactory); return validatorFactory.create(graph, var); } protected void doModify(final String label) { if(label.startsWith("=")) { session.asyncRequest(new WriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { variable.setPropertyValue(graph, Variables.EXPRESSION, label.substring(1).trim()); } }); } else { if(initialValue.startsWith("=")) try { session.syncRequest(new WriteRequest() { @Override public void perform(WriteGraph graph) throws DatabaseException { variable.setPropertyValue(graph, Variables.EXPRESSION, null); } }); } catch (DatabaseException e) { e.printStackTrace(); } session.asyncRequest(new VariableWrite(variable, label, null, targetUnit), new Callback() { @Override public void run(DatabaseException parameter) { if (parameter != null) ErrorLogger.defaultLogError(parameter); else modifySuccessful(); } }); } } protected void modifySuccessful() { } protected String getInitialValue(ReadGraph graph, Datatype dataType) throws DatabaseException { // TODO: more generic support through DataValuePrinter Object value = variable.getPossibleValue(graph); if(value == null) return ""; String unit = Variables.getPossibleUnit(graph, variable); //String unit = variable.getPossiblePropertyValue(graph, Variables.UNIT); if(value instanceof Double && unit != null && targetUnit != null) { IUnitConverter converter = VariableWrite.converter(unit, targetUnit); value = converter.convert((Double)value); } return FormattingUtils.US.engineeringFormat(value); } @Override public String getValue() { return initialValue; } @Override public String isValid(String label) { if (label.startsWith("=")) return null; if (modifierFailed != null) return "Could not resolve validator for this value, modification denied. Reason: " + modifierFailed.getMessage(); if (variableInputValidator != null) return variableInputValidator.isValid(label); try { if (binding instanceof StringBinding) { // Use the binding to validate strings since they might have // mime-types and/or patterns attached. if (binding instanceof MutableStringBinding) { binding.assertInstaceIsValid(new MutableString(label)); } else { binding.assertInstaceIsValid(label); } } else { if (binding instanceof NumberBinding) { // Numbers are always parsed in US locale, although both ',' and // '.' are accepted as decimal separators. label = label.replace(",", "."); //Object value = binding.parseValue(label, new DataValueRepository()); Object value = PrimitiveValueParser.parse(label, datatype); NumberType numberType = (NumberType) datatype; Range range = numberType.getRange(); if (range != null && !range.contains((Number) value)) return "Value is not in range " + range; } else { binding.parseValue(label, new DataValueRepository()); } } return null; } catch (IllegalArgumentException e) { // Parsing failed. return e.getLocalizedMessage(); } catch (DataTypeSyntaxError e) { // Failed to parse label with binding return e.getLocalizedMessage(); } catch (BindingException e) { return e.getLocalizedMessage(); } } @Override public final void modify(String label) { if (modifierFailed != null) // Should never end up here, isValid should prevent it. throw new Error("modifier failed: " + modifierFailed.getMessage()); doModify(label); } };