| /******************************************************************************* |
| * Copyright (c) 2005, 2017 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.equinox.metatype.impl; |
| |
| import java.io.IOException; |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.util.*; |
| import org.eclipse.equinox.metatype.EquinoxAttributeDefinition; |
| import org.eclipse.equinox.metatype.impl.Persistence.Reader; |
| import org.eclipse.equinox.metatype.impl.Persistence.Writer; |
| import org.eclipse.osgi.util.NLS; |
| import org.osgi.service.metatype.AttributeDefinition; |
| |
| /** |
| * Implementation of AttributeDefintion |
| */ |
| public class AttributeDefinitionImpl extends LocalizationElement implements EquinoxAttributeDefinition, Cloneable { |
| |
| private final String _name; |
| private final String _id; |
| private final String _description; |
| private final int _cardinality; |
| private final int _dataType; |
| private final Object _minValue; |
| private final Object _maxValue; |
| private final boolean _isRequired; |
| |
| private final LogTracker logger; |
| private final ExtendableHelper helper; |
| |
| private volatile String[] _defaults = null; |
| private volatile ArrayList<String> _values = new ArrayList<String>(7); |
| private volatile ArrayList<String> _labels = new ArrayList<String>(7); |
| |
| /** |
| * Constructor of class AttributeDefinitionImpl. |
| */ |
| public AttributeDefinitionImpl(String id, String name, String description, int type, int cardinality, Object min, Object max, boolean isRequired, String localization, LogTracker logger, Map<String, Map<String, String>> extensionAttributes) { |
| this(id, name, description, type, cardinality, min, max, isRequired, localization, logger, new ExtendableHelper(extensionAttributes)); |
| } |
| |
| private AttributeDefinitionImpl(String id, String name, String description, int type, int cardinality, Object min, Object max, boolean isRequired, String localization, LogTracker logger, ExtendableHelper helper) { |
| super(localization); |
| this._id = id; |
| this._name = name; |
| this._description = description; |
| this._dataType = type; |
| this._cardinality = cardinality; |
| this._minValue = min; |
| this._maxValue = max; |
| this._isRequired = isRequired; |
| this.logger = logger; |
| this.helper = helper; |
| } |
| |
| /* |
| * |
| */ |
| public Object clone() { |
| |
| AttributeDefinitionImpl ad = new AttributeDefinitionImpl(_id, _name, _description, _dataType, _cardinality, _minValue, _maxValue, _isRequired, getLocalization(), logger, helper); |
| |
| String[] curDefaults = _defaults; |
| if (curDefaults != null) { |
| ad.setDefaultValue(curDefaults.clone()); |
| } |
| |
| @SuppressWarnings("unchecked") |
| ArrayList<String> labels = (ArrayList<String>) _labels.clone(); |
| @SuppressWarnings("unchecked") |
| ArrayList<String> values = (ArrayList<String>) _values.clone(); |
| ad.setOption(labels, values, false); |
| |
| return ad; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.metatype.AttributeDefinition#getName() |
| */ |
| public String getName() { |
| return getLocalized(_name); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.metatype.AttributeDefinition#getID() |
| */ |
| public String getID() { |
| return _id; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.metatype.AttributeDefinition#getDescription() |
| */ |
| public String getDescription() { |
| return getLocalized(_description); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.metatype.AttributeDefinition#getCardinality() |
| */ |
| public int getCardinality() { |
| return _cardinality; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.metatype.AttributeDefinition#getType() |
| */ |
| public int getType() { |
| return _dataType; |
| } |
| |
| /** |
| * Method to get the required flag of AttributeDefinition. |
| */ |
| boolean isRequired() { |
| return _isRequired; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.metatype.AttributeDefinition#getOptionLabels() |
| */ |
| public String[] getOptionLabels() { |
| |
| List<String> curLabels = _labels; |
| if (curLabels.isEmpty()) { |
| return null; |
| } |
| |
| String[] returnedLabels = new String[curLabels.size()]; |
| int i = 0; |
| for (String labelKey : curLabels) { |
| returnedLabels[i] = getLocalized(labelKey); |
| i++; |
| } |
| return returnedLabels; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.metatype.AttributeDefinition#getOptionValues() |
| */ |
| public String[] getOptionValues() { |
| List<String> curValues = _values; |
| |
| if (curValues.isEmpty()) { |
| return null; |
| } |
| |
| return curValues.toArray(MetaTypeInformationImpl.emptyStringArray); |
| } |
| |
| /** |
| * Method to set the Option values of AttributeDefinition. |
| */ |
| void setOption(ArrayList<String> labels, ArrayList<String> values, boolean needValidation) { |
| if ((labels == null) || (values == null)) { |
| logger.log(LogTracker.LOG_ERROR, NLS.bind(MetaTypeMsg.NULL_OPTIONS, getID())); |
| return; |
| } |
| if (labels.size() != values.size()) { |
| logger.log(LogTracker.LOG_ERROR, NLS.bind(MetaTypeMsg.INCONSISTENT_OPTIONS, getID())); |
| return; |
| } |
| if (needValidation) { |
| for (int index = 0; index < values.size(); index++) { |
| ValueTokenizer vt = new ValueTokenizer(values.get(index), logger); |
| values.set(index, vt.getValuesAsString()); |
| String reason = vt.validate(this); |
| if ((reason != null) && reason.length() > 0) { |
| logger.log(LogTracker.LOG_WARNING, NLS.bind(MetaTypeMsg.INVALID_OPTIONS, new Object[] {values.get(index), getID(), reason})); |
| labels.remove(index); |
| values.remove(index); |
| index--; // Because this one has been removed. |
| } |
| } |
| } |
| _labels = labels; |
| _values = values; |
| } |
| |
| boolean containsInvalidValue(String value) { |
| List<String> curValues = _values; |
| return !curValues.isEmpty() && !curValues.contains(value); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.osgi.service.metatype.AttributeDefinition#getDefaultValue() |
| */ |
| public String[] getDefaultValue() { |
| return _defaults; |
| } |
| |
| /** |
| * Method to set the default value of AttributeDefinition. |
| * The given parameter is a comma delimited list needed to be parsed. |
| */ |
| void setDefaultValue(String defaults_str, boolean needValidation) { |
| ValueTokenizer vt = new ValueTokenizer(defaults_str, logger); |
| String reason = vt.validate(this); |
| if ((reason != null) && reason.length() > 0) { |
| logger.log(LogTracker.LOG_WARNING, NLS.bind(MetaTypeMsg.INVALID_DEFAULTS, new Object[] {vt.getValuesAsString(), getID(), reason})); |
| return; |
| } |
| String[] defaults = vt.getValuesAsArray(); |
| // If the default value is a single empty string and cardinality != 0, the default value must become String[0]. |
| // We know the cardinality has already been set in the constructor. |
| if (_cardinality != 0 && defaults.length == 1 && defaults[0].length() == 0) |
| setDefaultValue(new String[0]); |
| else |
| setDefaultValue(vt.getValuesAsArray()); |
| } |
| |
| /** |
| * Method to set the default value of AttributeDefinition. |
| * The given parameter is a String array of multi values. |
| */ |
| private void setDefaultValue(String[] defaults) { |
| _defaults = defaults; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * In order to be valid, a value must pass all of the following tests. |
| * (1) The value must not be null. |
| * (2) The value must be convertible into the attribute definition's type. |
| * (3) The following relation must hold: min <= value <= max, if either min or max was specified. |
| * (4) If options were specified, the value must be equal to one of them. |
| * |
| * Note this method will never return null to indicate there's no validation |
| * present. The type compatibility check can always be performed. |
| * |
| * @see org.osgi.service.metatype.AttributeDefinition#validate(java.lang.String) |
| */ |
| public String validate(String value) { |
| ValueTokenizer vt = new ValueTokenizer(value, logger); |
| return vt.validate(this); |
| } |
| |
| public Map<String, String> getExtensionAttributes(String schema) { |
| return helper.getExtensionAttributes(schema); |
| } |
| |
| public Set<String> getExtensionUris() { |
| return helper.getExtensionUris(); |
| } |
| |
| public String getMax() { |
| return _maxValue == null ? null : String.valueOf(_maxValue); |
| } |
| |
| public String getMin() { |
| return _minValue == null ? null : String.valueOf(_minValue); |
| } |
| |
| Object getMaxValue() { |
| return _maxValue; |
| } |
| |
| Object getMinValue() { |
| return _minValue; |
| } |
| |
| void getStrings(Set<String> strings) { |
| String[] curDefaults = _defaults; |
| if (curDefaults != null) { |
| for (String string : curDefaults) { |
| strings.add(string); |
| } |
| } |
| strings.add(_description); |
| strings.add(_id); |
| strings.add(_name); |
| strings.add(getLocalization()); |
| for (String string : _values) { |
| strings.add(string); |
| } |
| |
| for (String string : _labels) { |
| strings.add(string); |
| } |
| helper.getStrings(strings); |
| if (getType() == AttributeDefinition.STRING || getType() == AttributeDefinition.PASSWORD) { |
| if (_maxValue != null) { |
| strings.add(getMax()); |
| } |
| if (_minValue != null) { |
| strings.add(getMin()); |
| } |
| } |
| } |
| |
| public static AttributeDefinitionImpl load(Reader reader, LogTracker logger) throws IOException { |
| String id = reader.readString(); |
| String description = reader.readString(); |
| String name = reader.readString(); |
| int type = reader.readInt(); |
| int cardinality = reader.readInt(); |
| boolean isRequired = reader.readBoolean(); |
| String localization = reader.readString(); |
| |
| String[] defaults = null; |
| if (reader.readBoolean()) { |
| int numDefaults = reader.readInt(); |
| defaults = new String[numDefaults]; |
| for (int i = 0; i < numDefaults; i++) { |
| defaults[i] = reader.readString(); |
| } |
| } |
| int numLabels = reader.readInt(); |
| ArrayList<String> labels = new ArrayList<>(numLabels); |
| for (int i = 0; i < numLabels; i++) { |
| labels.add(reader.readString()); |
| } |
| int numValues = reader.readInt(); |
| ArrayList<String> values = new ArrayList<>(); |
| for (int i = 0; i < numValues; i++) { |
| values.add(reader.readString()); |
| } |
| ExtendableHelper helper = ExtendableHelper.load(reader); |
| Object min = readMinMax(type, reader); |
| Object max = readMinMax(type, reader); |
| |
| AttributeDefinitionImpl result = new AttributeDefinitionImpl(id, name, description, type, cardinality, min, max, isRequired, localization, logger, helper); |
| result.setDefaultValue(defaults); |
| result.setOption(labels, values, false); |
| return result; |
| } |
| |
| public void write(Writer writer) throws IOException { |
| writer.writeString(_id); |
| writer.writeString(_description); |
| writer.writeString(_name); |
| writer.writeInt(_dataType); |
| writer.writeInt(_cardinality); |
| writer.writeBoolean(_isRequired); |
| writer.writeString(getLocalization()); |
| String[] curDefaults = _defaults; |
| if (curDefaults == null) { |
| writer.writeBoolean(false); |
| } else { |
| writer.writeBoolean(true); |
| writer.writeInt(curDefaults.length); |
| for (String defaultValue : curDefaults) { |
| writer.writeString(defaultValue); |
| } |
| } |
| List<String> curLabels = _labels; |
| writer.writeInt(curLabels.size()); |
| for (String label : curLabels) { |
| writer.writeString(label); |
| } |
| List<String> curValues = _values; |
| writer.writeInt(curValues.size()); |
| for (String value : curValues) { |
| writer.writeString(value); |
| } |
| helper.write(writer); |
| writeMinMax(_minValue, writer); |
| writeMinMax(_maxValue, writer); |
| } |
| |
| @SuppressWarnings("deprecation") |
| private static Object readMinMax(int dataType, Reader reader) throws IOException { |
| boolean isNull = reader.readBoolean(); |
| if (isNull) { |
| return null; |
| } |
| switch (dataType) { |
| // PASSWORD should be treated like STRING. |
| case AttributeDefinition.PASSWORD : |
| case AttributeDefinition.STRING : |
| return reader.readString(); |
| case AttributeDefinition.LONG : |
| return reader.readLong(); |
| case AttributeDefinition.INTEGER : |
| return reader.readInt(); |
| case AttributeDefinition.SHORT : |
| return reader.readShort(); |
| case AttributeDefinition.CHARACTER : |
| return reader.readCharacter(); |
| case AttributeDefinition.BYTE : |
| return reader.readByte(); |
| case AttributeDefinition.DOUBLE : |
| return reader.readDouble(); |
| case AttributeDefinition.FLOAT : |
| return reader.readFloat(); |
| case AttributeDefinition.BIGINTEGER : |
| return new BigInteger(reader.readString()); |
| case AttributeDefinition.BIGDECIMAL : |
| return new BigDecimal(reader.readString()); |
| case AttributeDefinition.BOOLEAN : |
| return reader.readBoolean(); |
| default : |
| return reader.readString(); |
| } |
| } |
| |
| @SuppressWarnings("deprecation") |
| private void writeMinMax(Object v, Writer writer) throws IOException { |
| if (v == null) { |
| writer.writeBoolean(true); |
| return; |
| } |
| writer.writeBoolean(false); |
| switch (_dataType) { |
| // PASSWORD should be treated like STRING. |
| case AttributeDefinition.PASSWORD : |
| case AttributeDefinition.STRING : |
| writer.writeString((String) v); |
| return; |
| case AttributeDefinition.LONG : |
| writer.writeLong((Long) v); |
| return; |
| case AttributeDefinition.INTEGER : |
| writer.writeInt((Integer) v); |
| return; |
| case AttributeDefinition.SHORT : |
| writer.writeShort((Short) v); |
| return; |
| case AttributeDefinition.CHARACTER : |
| writer.writeCharacter((Character) v); |
| return; |
| case AttributeDefinition.BYTE : |
| writer.writeByte((Byte) v); |
| return; |
| case AttributeDefinition.DOUBLE : |
| writer.writeDouble((Double) v); |
| return; |
| case AttributeDefinition.FLOAT : |
| writer.writeFloat((Float) v); |
| return; |
| case AttributeDefinition.BIGINTEGER : |
| writer.writeString(v.toString()); |
| return; |
| case AttributeDefinition.BIGDECIMAL : |
| writer.writeString(v.toString()); |
| return; |
| case AttributeDefinition.BOOLEAN : |
| writer.writeBoolean((Boolean) v); |
| return; |
| default : |
| // Unknown data type |
| writer.writeString(String.valueOf(v)); |
| return; |
| } |
| } |
| |
| } |