/* * Copyright (c) 2017 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.magma.js; import java.util.Collection; import java.util.Date; import java.util.List; import javax.annotation.Nullable; import javax.validation.constraints.NotNull; import org.mozilla.javascript.Context; import org.mozilla.javascript.ScriptRuntime; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.Undefined; import org.obiba.magma.Coordinate; import org.obiba.magma.MagmaDate; import org.obiba.magma.Value; import org.obiba.magma.ValueType; import org.obiba.magma.type.BooleanType; import org.obiba.magma.type.DateType; import org.obiba.magma.type.LineStringType; import org.obiba.magma.type.PointType; import org.obiba.magma.type.PolygonType; import org.obiba.magma.type.TextType; /** * A {@code Scriptable} implementation for {@code Value} objects. * <p/> * Methods available on the {@code ScriptableValue} instances are built by the {@code ScriptableValuePrototypeFactory}. * It allows extending the methods of {@code ScriptableValue}. * * @see ScriptableValuePrototypeFactory */ public class ScriptableValue extends ScriptableObject { private static final long serialVersionUID = -4342110775412157728L; static final String VALUE_CLASS_NAME = "Value"; @NotNull private Value value; private String unit; /** * No-arg ctor for building the prototype */ @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", justification = "Needed by ScriptableValuePrototypeFactory") ScriptableValue() { } public ScriptableValue(Scriptable scope, @NotNull Value value, @Nullable String unit) { super(scope, ScriptableObject.getClassPrototype(scope, VALUE_CLASS_NAME)); //noinspection ConstantConditions if(value == null) throw new IllegalArgumentException("value cannot be null"); this.value = value; this.unit = unit; } public ScriptableValue(Scriptable scope, @NotNull Value value) { this(scope, value, null); } public boolean hasUnit() { return unit != null; } public String getUnit() { return unit; } @Override public String getClassName() { return VALUE_CLASS_NAME; } @Nullable @Override public Object getDefaultValue(Class<?> typeHint) { if(value.isSequence()) { return value.asSequence().toString(); } ValueType valueType = value.getValueType(); boolean isNull = value.isNull(); if(valueType.isDateTime()) { if(isNull) { return Context.toObject(null, this); } double jsDate = valueType == DateType.get() ? ((MagmaDate) value.getValue()).asDate().getTime() : ((Date) value.getValue()).getTime(); return Context.toObject(ScriptRuntime.wrapNumber(jsDate), this); } if(valueType.isGeo()) { return isNull ? Context.toObject(null, this) : getGeoDefaultValue(valueType, value.getValue()); } if(valueType.isNumeric()) { return Context.toNumber(isNull ? Undefined.instance : value.getValue()); } if(valueType.equals(BooleanType.get())) { return Context.toBoolean(isNull ? null : value.getValue()); } if(valueType.equals(TextType.get())) { return Context.toString(isNull ? null : value.getValue()); } return value.getValue(); } @SuppressWarnings("unchecked") private Object getGeoDefaultValue(ValueType type, Object defaultValue) { if(PointType.get().equals(type)) { return ((Coordinate) defaultValue).toArray(); } if(LineStringType.get().equals(type)) { return getLineDefaultValue((Collection<Coordinate>) defaultValue); } if(PolygonType.get().equals(type)) { return getPolygonDefaultValue((Collection<List<Coordinate>>) defaultValue); } return defaultValue; } private double[][] getLineDefaultValue(Collection<Coordinate> line) { double[][] dline = new double[line.size()][]; int i = 0; for(Coordinate coordinate : line) { dline[i++] = coordinate.toArray(); } return dline; } private double[][][] getPolygonDefaultValue(Collection<List<Coordinate>> polygon) { double[][][] dpolygon = new double[polygon.size()][][]; int i = 0; for(List<Coordinate> line : polygon) { dpolygon[i++] = getLineDefaultValue(line); } return dpolygon; } @NotNull public Value getValue() { return value; } public ValueType getValueType() { return getValue().getValueType(); } /** * Check if the value contains or is equal to (if not a sequence) the given value. * * @param testValue * @return */ public boolean contains(Value testValue) { return getValue().isSequence() // ? getValue().asSequence().contains(testValue) // : getValue().equals(testValue); } /** * Get the position of the given value in the sequence or 0 if not a sequence. * * @param testValue * @return -1 if not found */ public int indexOf(Value testValue) { return getValue().isSequence() // ? getValue().asSequence().indexOf(testValue) // : getValue().equals(testValue) ? 0 : -1; } /** * Get the last position of the given value in the sequence or 0 if not a sequence. * * @param testValue * @return -1 if not found */ public int lastIndexOf(Value testValue) { return getValue().isSequence() // ? getValue().asSequence().lastIndexOf(testValue) // : getValue().equals(testValue) ? 0 : -1; } /* * (non-Javadoc) * * @see java.lang.Object#toString() */ @Nullable @Override public String toString() { return getValue().toString(); } }