Commit c84c7008 authored by Nico Mack's avatar Nico Mack

Introduction of ScriptableCondition

parent 46f6525c
......@@ -17,6 +17,7 @@ MAXIMUM_VALUE_NODE=maximumValue
MINIMUM_VALUE_NODE=minimumValue
MODIFY_VALUE_ON_ROTATION_NODE=modifyValueOnRotation
MULTITURN_NODE=multiTurn
POSITIONS_NODE=positions
RANGE_SEPARATOR_ATTRIBUTE=rangeSeparator
REFERENCE_NODE=reference
RELATIVE_NODE=relative
......
......@@ -15,6 +15,7 @@ import java.util.Properties;
abstract public class Executor {
protected CharArrayWriter executionErrors;
protected boolean verbose;
private static Logger LOGGER = LoggerFactory.getLogger(Executor.class.getSimpleName());
......@@ -26,11 +27,13 @@ abstract public class Executor {
public Executor() {
executionErrors = new CharArrayWriter();
verbose = true;
}
@SuppressWarnings("unused")
public Executor(Properties properties) {
executionErrors = new CharArrayWriter();
verbose = true;
}
protected static void addLibraryPath(String pathToAdd) throws Exception {
......@@ -62,6 +65,10 @@ abstract public class Executor {
executionErrors.reset();
}
public void setVerbosity(boolean verbose) {
this.verbose = verbose;
}
abstract public void resetContext();
@SuppressWarnings("unused")
......
......@@ -36,7 +36,7 @@ public class JavascriptExecutor extends Executor {
Variable<?> variable = declaration.getVariable();
engine.put(variable.getName(), variable.getValue());
if (LOGGER.isInfoEnabled()) {
if (verbose && LOGGER.isInfoEnabled()) {
LOGGER.info("Parameter {} = {}", variable.getName(), variable.getValue()); //$NON-NLS-1$
}
}
......@@ -47,7 +47,7 @@ public class JavascriptExecutor extends Executor {
long elapsed = System.currentTimeMillis();
result = engine.eval(script);
elapsed = System.currentTimeMillis() - elapsed;
if (LOGGER.isInfoEnabled()) {
if (verbose && LOGGER.isInfoEnabled()) {
LOGGER.info("Eval => {} | took {} ms!", script, elapsed); //$NON-NLS-1$
}
} catch (ScriptException exception) {
......@@ -63,7 +63,7 @@ public class JavascriptExecutor extends Executor {
public Variable<?> get(Declaration declaration) {
Variable<?> variable = declaration.getVariable();
variable.setValueFromObject(result);
if (LOGGER.isInfoEnabled()) {
if (verbose && LOGGER.isInfoEnabled()) {
LOGGER.info("Result {} = {}", variable.getName(), result); //$NON-NLS-1$
}
return variable;
......
/**
* Copyright Luxembourg Institute of Science and Technology, 2018. All rights reserved. If you wish
* to use this code for any purpose, please contact the author(s).
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package lu.list.itis.dkd.tui.event.conditional;
import lu.list.itis.dkd.tui.cps.system.Declaration;
import lu.list.itis.dkd.tui.cps.system.Equation;
import lu.list.itis.dkd.tui.cps.system.Mapping;
import lu.list.itis.dkd.tui.cps.system.VariableBased;
import lu.list.itis.dkd.tui.cps.system.executor.JavascriptExecutor;
import lu.list.itis.dkd.tui.cps.variable.BooleanVariable;
import lu.list.itis.dkd.tui.cps.variable.Variable;
import lu.list.itis.dkd.tui.event.conditional.builder.BaseScriptableConditionBuilder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
* @param <T>
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public class ScriptableCondition<T> extends Condition<T> implements VariableBased {
private Equation condition;
private String expression;
private HashMap<String, Variable<?>> parameters;
private BooleanVariable satisfied = new BooleanVariable(SATISFIED, false);
// ***************************************************************************
// * Constants *
// ***************************************************************************
private static final String SATISFIED = "Satisfied"; //$NON-NLS-1$
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
public ScriptableCondition(String expression) {
this.expression = expression;
}
// ---------------------------------------------------------------------------
public ScriptableCondition(BaseScriptableConditionBuilder<?> builder) {
this.expression = builder.expression;
this.parameters = builder.parameters;
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitives(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
private void setupCondition(String expression) {
LinkedHashSet<Declaration> inputs = new LinkedHashSet<>();
LinkedHashSet<Declaration> outputs = new LinkedHashSet<>();
for (Variable<?> parameter : parameters.values()) {
inputs.add(new Declaration(parameter));
}
outputs.add(new Declaration(satisfied));
condition = new Equation(ScriptableCondition.class.getName(), new Mapping(inputs, outputs), expression);
JavascriptExecutor executor = new JavascriptExecutor();
executor.setVerbosity(false);
condition.setExecutor(executor);
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
public void setCondition(Condition<T> condition) {
// Nothing to do here.
}
// ---------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
public boolean satisfiedBy(T candidate) {
this.condition.evaluate();
return this.satisfied.getValue();
}
// ---------------------------------------------------------------------------
@Override
public List<Variable<?>> connectWithSystemVariables(Map<String, Variable<?>> systemVariables) {
List<Variable<?>> connected = new ArrayList<>();
if (systemVariables != null) {
for (String identifier : parameters.keySet()) {
if (systemVariables.containsKey(identifier)) {
Variable<?> variable = systemVariables.get(identifier);
this.parameters.put(identifier, variable);
connected.add(variable);
}
}
}
this.setupCondition(expression);
return connected;
}
// ---------------------------------------------------------------------------
@Override
public List<Variable<?>> getDeclaredVariables() {
return new ArrayList<>(parameters.values());
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * End of Class *
// ***************************************************************************
// ---------------------------------------------------------------------------
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2018. All rights reserved. If you wish
* to use this code for any purpose, please contact the author(s).
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package lu.list.itis.dkd.tui.event.conditional.builder;
import lu.list.itis.dkd.dbc.annotation.Nullable;
import lu.list.itis.dkd.tui.bootstrapping.BootstrapCallback;
import lu.list.itis.dkd.tui.bootstrapping.BootstrapContext;
import lu.list.itis.dkd.tui.bootstrapping.BootstrappingUtils;
import lu.list.itis.dkd.tui.cps.utility.EquationSystemBundle;
import lu.list.itis.dkd.tui.cps.variable.NumericalVariable;
import lu.list.itis.dkd.tui.cps.variable.Variable;
import lu.list.itis.dkd.tui.event.conditional.ScriptableCondition;
import lu.list.itis.dkd.tui.event.conditional.builder.BaseConditionBuilder;
import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.utility.Externalization;
import org.jdom2.Element;
import java.util.HashMap;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
// ***************************************************************************
// * Class Definition and Members *
// ***************************************************************************
public abstract class BaseScriptableConditionBuilder<B extends BaseScriptableConditionBuilder<B>> extends BaseConditionBuilder<B> {
public String expression;
public HashMap<String, Variable<?>> parameters;
// ***************************************************************************
// * Constants *
// ***************************************************************************
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s) *
// ***************************************************************************
// ---------------------------------------------------------------------------
/**
* @param rootElement
* @throws BuildException
*/
// ---------------------------------------------------------------------------
public BaseScriptableConditionBuilder(Element rootElement) throws BuildException {
super(rootElement);
this.buildFromBootstrap(rootElement, null, null);
}
// ---------------------------------------------------------------------------
/**
* @param rootElement
* @throws BuildException
*/
// ---------------------------------------------------------------------------
public BaseScriptableConditionBuilder(Element rootElement, BootstrapContext context, BootstrapCallback callback) throws BuildException {
super(rootElement, context, callback);
this.buildFromBootstrap(rootElement, context, callback);
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Primitives *
// ***************************************************************************
// ---------------------------------------------------------------------------
private void buildFromBootstrap(@Nullable Element rootElement, BootstrapContext context, BootstrapCallback callback) throws BuildException {
expression = BootstrappingUtils.getContentAsString(rootElement, Externalization.EXPRESSION_NODE, BootstrappingUtils.MANDATORY, null, context);
parameters = new HashMap<>();
for (Element parameterNode : rootElement.getChild(EquationSystemBundle.PARAMETERS_ELEMENT).getChildren(EquationSystemBundle.PARAMETER_ELEMENT)) {
String identifier = BootstrappingUtils.getAttributeAsString(parameterNode, EquationSystemBundle.NAME_ATTRIBUTE, BootstrappingUtils.MANDATORY, null);
double initial = BootstrappingUtils.getAttributeAsDouble(parameterNode, EquationSystemBundle.INITIAL_ATTRIBUTE, BootstrappingUtils.OPTIONAL, 0d);
parameters.put(identifier, new NumericalVariable(identifier, Externalization.EMPTY_STRING, initial));
}
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Class Body *
// ***************************************************************************
// ---------------------------------------------------------------------------
/** {@inheritDoc} */
@Override
public abstract ScriptableCondition build();
}
/**
* Copyright Luxembourg Institute of Science and Technology, 2018. All rights reserved. If you wish
* to use this code for any purpose, please contact the author(s).
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
* WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package lu.list.itis.dkd.tui.event.conditional.builder;
import lu.list.itis.dkd.tui.bootstrapping.BootstrapCallback;
import lu.list.itis.dkd.tui.bootstrapping.BootstrapContext;
import lu.list.itis.dkd.tui.event.conditional.ScriptableCondition;
import lu.list.itis.dkd.tui.exception.BuildException;
import org.jdom2.Element;
/**
* @author mack
* @since [major].[minor]
* @version [major].[minor].[micro]
*/
public class ScriptableConditionBuilder extends BaseScriptableConditionBuilder<ScriptableConditionBuilder> {
/**
* Constructor initializing all fields from an {@link Element} containing as child elements all the
* information on fields to initialize.
*
* @param rootElement
* The element harbouring, on child nodes, the necessary information to initialize all fields
* of the builder.
* @throws BuildException
* Thrown when any of the fields fail to populate due to an error in reading information
* from the XML file.
*/
public ScriptableConditionBuilder(Element rootElement) throws BuildException {
super(rootElement);
}
/**
* Constructor initializing all fields from an {@link Element} containing as child elements all the
* information on fields to initialize.
*
* @param rootElement
* The element harbouring, on child nodes, the necessary information to initialize all fields
* of the builder.
* @throws BuildException
* Thrown when any of the fields fail to populate due to an error in reading information
* from the XML file.
*/
public ScriptableConditionBuilder(Element rootElement, BootstrapContext context, BootstrapCallback callback) throws BuildException {
super(rootElement, context, callback);
}
/** {@inheritDoc} */
@Override
public ScriptableCondition build() {
// TODO Auto-generated method stub
return new ScriptableCondition(this);
}
}
......@@ -53,6 +53,8 @@ public class CpsNamespace extends NLS {
public static String MODIFY_VALUE_ON_ROTATION_NODE;
public static String MULTITURN_NODE;
public static String POSITIONS_NODE;
public static String RANGE_SEPARATOR_ATTRIBUTE;
public static String REFERENCE_NODE;
public static String RELATIVE_NODE;
......
package lu.list.itis.dkd.tui.widget;
import lu.list.itis.dkd.tui.adapter.TangibleObject;
import lu.list.itis.dkd.tui.cps.system.VariableBased;
import lu.list.itis.dkd.tui.cps.variable.Variable;
import lu.list.itis.dkd.tui.event.conditional.Condition;
import lu.list.itis.dkd.tui.event.conditional.Conditional;
import lu.list.itis.dkd.tui.utility.AngleUtils;
import lu.list.itis.dkd.tui.utility.Point;
import lu.list.itis.dkd.tui.utility.ValueRange;
......@@ -35,6 +38,7 @@ public class SelectorWidget extends ValueWidget {
private double segmentSpan;
private List<ValueRange<Double>> segmentSpans;
private List<SelectableCorona> selectableCoronas;
// ***************************************************************************
// * Constants *
......@@ -44,6 +48,24 @@ public class SelectorWidget extends ValueWidget {
private static final Logger LOGGER = LoggerFactory.getLogger(SelectorWidget.class.getSimpleName());
// ***************************************************************************
// * Inner Classs *
// ***************************************************************************
private class IndexCondition extends Condition<Integer> {
private int index;
public IndexCondition(int index) {
this.index = index;
}
/** {@inheritDoc} */
@Override
public boolean satisfiedBy(Integer position) {
return (this.index == position);
}
}
// ---------------------------------------------------------------------------
// ***************************************************************************
// * Constructor(s) *
......@@ -59,7 +81,10 @@ public class SelectorWidget extends ValueWidget {
public SelectorWidget(SelectorWidgetBuilder<?> builder) {
super(builder);
this.numberOfPositions = this.getNumberOfPositions();
selectableCoronas = getCoronas(SelectableCorona.class);
this.numberOfPositions = (builder.numberOfPositions > 0) ? builder.numberOfPositions : this.getNumberOfPositions();
this.presetPosition = builder.preselect;
this.buildFromProperties();
......@@ -77,7 +102,9 @@ public class SelectorWidget extends ValueWidget {
public SelectorWidget(SelectorWidget original) {
super(original);
this.numberOfPositions = original.numberOfPositions;
selectableCoronas = getCoronas(SelectableCorona.class);
this.numberOfPositions = (original.numberOfPositions > 0) ? original.numberOfPositions : this.getNumberOfPositions();
this.presetPosition = original.presetPosition;
this.buildFromProperties();
......@@ -124,7 +151,7 @@ public class SelectorWidget extends ValueWidget {
// ---------------------------------------------------------------------------
protected int getNumberOfPositions() {
List<SelectableCorona> selectableCoronas = getCoronas(SelectableCorona.class);
Multimap<Integer, SelectableCorona> alreadySeen = TreeMultimap.create();
boolean duplicateIndices = false;
......@@ -135,6 +162,10 @@ public class SelectorWidget extends ValueWidget {
duplicateIndices = identical.getClass().equals(corona.getClass());
}
}
IndexCondition condition = new IndexCondition(index);
corona.setCondition(condition);
alreadySeen.put(index, corona);
}
......@@ -149,7 +180,13 @@ public class SelectorWidget extends ValueWidget {
@Override
protected void updateFromRotation(double angle, Direction direction) {
// Empty to disable features implemented in inherited class
if (this.multiTurn) {
super.updateFromRotation(angle, direction);
if (this.variable.wasModified()) {
int position = this.variable.getValue().intValue();
selectableCoronas.forEach(indexed -> indexed.satisfiedBy(position));
}
}
}
// ---------------------------------------------------------------------------
......@@ -195,7 +232,11 @@ public class SelectorWidget extends ValueWidget {
protected void selectPosition(int position) {
if (position != currentPosition) {
this.getCoronas(SelectableCorona.class).forEach(indexed -> indexed.setSelected(indexed.getIndex() == position));
// this.getCoronas(SelectableCorona.class).forEach(indexed -> indexed.setSelected(indexed.getIndex()
// == position));
// this.getCoronas(SelectableCorona.class).forEach(indexed ->
// indexed.setSelected(indexed.satisfiedBy(position)));
selectableCoronas.forEach(indexed -> indexed.satisfiedBy(position));
if (variable != null) {
variable.setValue((double) position);
......@@ -233,6 +274,14 @@ public class SelectorWidget extends ValueWidget {
variable.notifyInputChangeListeners();
}
List<Conditional> conditionalCoronas = getCoronas(Conditional.class);
for (Conditional<?> corona : conditionalCoronas) {
Condition<?> condition = corona.getCondition();
if (condition instanceof VariableBased) {
((VariableBased) condition).connectWithSystemVariables(systemVariables);
}
}
return connected;
}
......@@ -267,7 +316,7 @@ public class SelectorWidget extends ValueWidget {
StateManager manager = objectStates.get(widgetId);
if (manager.isRotating()) {
if (!multiTurn && manager.isRotating()) {
int position = getCurrentPosition((float) this.dialAngle);
if (position != currentPosition) {
this.selectPosition(position);
......
......@@ -163,13 +163,6 @@ public class ValueWidget extends TetherableWidget implements InformationProvider
lastAngle = Double.NaN;
// if (!modifyValueOnRotation) {
// this.initialValue = getVariable().getValue();
// getVariable().setValue(0d);
// } else {
// this.initialValue = getVariable().getValue();
// }
this.initialValue = (!Double.isNaN(variable.getInitialValue())) ? variable.getInitialValue() : 0d;
withStopAngles = !(Double.isNaN(lowerStopAngle) || Double.isNaN(upperStopAngle));
......@@ -252,7 +245,7 @@ public class ValueWidget extends TetherableWidget implements InformationProvider
value = lowerBound + (stepSize * Math.floor((angle - lowerStopAngle) / stepAngle));
} else if (multiTurn) {
value = variable.getConstrainedValue();
if (!Double.isNaN(lastAngle)) {
if ((direction != null) && !Double.isNaN(lastAngle)) {
double wrapped = AngleUtils.wrapTwoPi(angle - lastAngle);
double degrees = Math.toDegrees(wrapped);
double delta = Math.abs(degrees * stepSize);
......
......@@ -5,6 +5,7 @@ import lu.list.itis.dkd.tui.bootstrapping.BootstrapCallback;
import lu.list.itis.dkd.tui.bootstrapping.BootstrapContext;
import lu.list.itis.dkd.tui.bootstrapping.BootstrappingUtils;
import lu.list.itis.dkd.tui.exception.BuildException;
import lu.list.itis.dkd.tui.utility.CpsNamespace;
import lu.list.itis.dkd.tui.utility.Externalization;
import lu.list.itis.dkd.tui.widget.SelectorWidget;
......@@ -24,6 +25,7 @@ import org.jdom2.Element;
public class SelectorWidgetBuilder<B extends SelectorWidgetBuilder<B>> extends BaseValueWidgetBuilder<B> {
public Integer numberOfPositions;
public Integer preselect;
/**
......@@ -35,12 +37,12 @@ public class SelectorWidgetBuilder<B extends SelectorWidgetBuilder<B>> extends B
}
/**
* Constructor initializing all fields from an {@link Element} containing as child elements all
* the information on fields to initialize.
* Constructor initializing all fields from an {@link Element} containing as child elements all the
* information on fields to initialize.
*
* @param rootElement
* The element harbouring, on child nodes, the necessary information to initialize all
* fields of the builder.
* The element harbouring, on child nodes, the necessary information to initialize all fields
* of the builder.
* @throws BuildException
* Thrown when any of the fields fail to populate due to an error in reading information
* from the XML file.
......@@ -51,12 +53,12 @@ public class SelectorWidgetBuilder<B extends SelectorWidgetBuilder<B>> extends B
}
/**
* Constructor initializing all fields from an {@link Element} containing as child elements all
* the information on fields to initialize.
* Constructor initializing all fields from an {@link Element} containing as child elements all the
* information on fields to initialize.
*
* @param rootElement
* The element harbouring, on child nodes, the necessary information to initialize all
* fields of the builder.
* The element harbouring, on child nodes, the necessary information to initialize all fields
* of the builder.
* @throws BuildException
* Thrown when any of the fields fail to populate due to an error in reading information
* from the XML file.
......@@ -74,6 +76,7 @@ public class SelectorWidgetBuilder<B extends SelectorWidgetBuilder<B>> extends B
* @throws BuildException
*/
private void buildFromBootstrap(@Nullable Element rootElement, BootstrapContext context, BootstrapCallback callback) throws BuildException {
numberOfPositions = BootstrappingUtils.getContentAsInteger(rootElement, CpsNamespace.POSITIONS_NODE, BootstrappingUtils.OPTIONAL, SelectorWidget.NONE, context);
preselect = BootstrappingUtils.getContentAsInteger(rootElement, Externalization.PRESELECT_NODE, BootstrappingUtils.OPTIONAL, SelectorWidget.NONE, context);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment