| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.felix.metatype; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.felix.metatype.internal.Activator; |
| import org.osgi.service.log.LogService; |
| import org.osgi.service.metatype.AttributeDefinition; |
| |
| /** |
| * The <code>AD</code> class represents the <code>AD</code> element of the |
| * meta type descriptor. |
| * |
| * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a> |
| */ |
| public class AD extends OptionalAttributes |
| { |
| |
| /** |
| * The message returned from the {@link #validate(String)} method if the |
| * value is not any of the specified {@link #getOptionValues() option values} |
| * (value is "%not a valid option"). |
| */ |
| public static final String VALIDATE_NOT_A_VALID_OPTION = "%not a valid option"; |
| |
| /** |
| * The message returned from the {@link #validate(String)} method if the |
| * value is invalid considering its type (value is "%invalid value"). |
| */ |
| public static final String VALIDATE_INVALID_VALUE = "%invalid value"; |
| |
| /** |
| * The message returned from the {@link #validate(String)} method if the |
| * value is greater than the specified {@link #getMax() maximum value} |
| * (value is "%greater than maximum"). |
| */ |
| public static final String VALIDATE_GREATER_THAN_MAXIMUM = "%greater than maximum"; |
| |
| /** |
| * The message returned from the {@link #validate(String)} method if the |
| * value is less than the specified {@link #getMin() minimum value} |
| * (value is "%less than minimum"). |
| */ |
| public static final String VALIDATE_LESS_THAN_MINIMUM = "%less than minimum"; |
| |
| /** |
| * The message returned from the {@link #validate(String)} method if the |
| * value is null or cannot be converted to an attribute value and a value |
| * is {@link #isRequired() required} (value is "%missing required value"). |
| */ |
| public static final String VALIDATE_MISSING = "%missing required value"; |
| |
| private String id; |
| private String name; |
| private String description; |
| private int type; |
| private int cardinality = 0; |
| private String[] optionLabels; |
| private String[] optionValues; |
| private String[] defaultValue; |
| private String min; |
| private String max; |
| private boolean isRequired = true; |
| |
| public String getID() |
| { |
| return id; |
| } |
| |
| public String getName() |
| { |
| return name; |
| } |
| |
| public String getDescription() |
| { |
| return description; |
| } |
| |
| public int getType() |
| { |
| return type; |
| } |
| |
| public int getCardinality() |
| { |
| return cardinality; |
| } |
| |
| public String[] getOptionLabels() |
| { |
| return optionLabels; |
| } |
| |
| public String[] getOptionValues() |
| { |
| return optionValues; |
| } |
| |
| public String[] getDefaultValue() |
| { |
| return defaultValue; |
| } |
| |
| public String getMin() |
| { |
| return min; |
| } |
| |
| public String getMax() |
| { |
| return max; |
| } |
| |
| public boolean isRequired() |
| { |
| return isRequired; |
| } |
| |
| /** |
| * Implements validation of the <code>valueString</code> and returns an |
| * indication of the validation result. |
| * |
| * @param valueString The string representation of the value to validate, |
| * can be <code>null</code>. |
| * |
| * @return <code>null</code> if no validation is performed, <tt>""</tt> if |
| * the value is accepted as valid, or a non-empty string |
| * indicating a validation problem was found. |
| * |
| * @see ADValidator#validate(AD, String) |
| * @see #VALIDATE_GREATER_THAN_MAXIMUM |
| * @see #VALIDATE_NOT_A_VALID_OPTION |
| * @see #VALIDATE_LESS_THAN_MINIMUM |
| * @see #VALIDATE_INVALID_VALUE |
| * @see #VALIDATE_MISSING |
| */ |
| public String validate(String valueString) |
| { |
| return ADValidator.validate(this, valueString); |
| } |
| |
| //--------- Setters for setting up this instance -------------------------- |
| |
| /** |
| * @param id the id to set |
| */ |
| public void setID(String id) |
| { |
| this.id = id; |
| } |
| |
| /** |
| * @param name the name to set |
| */ |
| public void setName(String name) |
| { |
| this.name = name; |
| } |
| |
| /** |
| * @param description the description to set |
| */ |
| public void setDescription(String description) |
| { |
| this.description = description; |
| } |
| |
| /** |
| * @param typeString the type to set |
| */ |
| public void setType(String typeString) |
| { |
| this.type = toType(typeString); |
| } |
| |
| /** |
| * @param cardinality the cardinality to set |
| */ |
| public void setCardinality(int cardinality) |
| { |
| this.cardinality = cardinality; |
| } |
| |
| /** |
| * @param options the options to set |
| */ |
| public void setOptions(Map options) |
| { |
| optionLabels = new String[options.size()]; |
| optionValues = new String[options.size()]; |
| int i = 0; |
| for (Iterator oi = options.entrySet().iterator(); oi.hasNext(); i++) |
| { |
| Map.Entry entry = (Map.Entry) oi.next(); |
| optionValues[i] = String.valueOf(entry.getKey()); |
| optionLabels[i] = String.valueOf(entry.getValue()); |
| } |
| } |
| |
| /** |
| * Sets the default value(s) for this AD. |
| * <p> |
| * NOTE: this method is depending on the value of {@link #getCardinality()}! Make sure that the |
| * cardinality is properly set <b>before</b> calling this method. |
| * </p> |
| * |
| * @param defaultValue the default value to set, as encoded string-value (using comma's as separator), can be <code>null</code>. |
| */ |
| public void setDefaultValue(String defaultValue) |
| { |
| setDefaultValue(splitList(defaultValue), Math.abs(this.cardinality)); |
| } |
| |
| /** |
| * @param min the min to set |
| */ |
| public void setMin(String min) |
| { |
| this.min = min; |
| } |
| |
| /** |
| * @param max the max to set |
| */ |
| public void setMax(String max) |
| { |
| this.max = max; |
| } |
| |
| /** |
| * @param isRequired the isRequired to set |
| */ |
| public void setRequired(boolean isRequired) |
| { |
| this.isRequired = isRequired; |
| } |
| |
| public static int toType(String typeString) |
| { |
| if ("String".equals(typeString)) |
| { |
| return AttributeDefinition.STRING; |
| } |
| else if ("Long".equals(typeString)) |
| { |
| return AttributeDefinition.LONG; |
| } |
| else if ("Double".equals(typeString)) |
| { |
| return AttributeDefinition.DOUBLE; |
| } |
| else if ("Float".equals(typeString)) |
| { |
| return AttributeDefinition.FLOAT; |
| } |
| else if ("Integer".equals(typeString)) |
| { |
| return AttributeDefinition.INTEGER; |
| } |
| else if ("Byte".equals(typeString)) |
| { |
| return AttributeDefinition.BYTE; |
| } |
| else if ("Character".equals(typeString) || "Char".equals(typeString)) |
| { |
| return AttributeDefinition.CHARACTER; |
| } |
| else if ("Boolean".equals(typeString)) |
| { |
| return AttributeDefinition.BOOLEAN; |
| } |
| else if ("Short".equals(typeString)) |
| { |
| return AttributeDefinition.SHORT; |
| } |
| else if ("Password".equals(typeString)) |
| { |
| return AttributeDefinition.PASSWORD; |
| } |
| |
| // finally fall back to string for illegal values |
| return AttributeDefinition.STRING; |
| } |
| |
| public static String[] splitList(String listString) |
| { |
| if (listString == null) |
| { |
| return null; |
| } |
| else if (listString.length() == 0) |
| { |
| return new String[] { "" }; |
| } |
| |
| List<String> strings = new ArrayList<>(); |
| StringBuilder sb = new StringBuilder(); |
| |
| int length = listString.length(); |
| boolean escaped = false; |
| int spaceCount = 0; |
| boolean start = true; |
| for (int i = 0; i < length; i++) |
| { |
| char ch = listString.charAt(i); |
| final boolean isWhitespace = Character.isWhitespace(ch); |
| if (start) |
| { |
| if (isWhitespace) |
| { |
| continue; |
| } |
| start = false; |
| } |
| if (ch == '\\') |
| { |
| if (!escaped) |
| { |
| escaped = true; |
| continue; |
| } |
| } |
| else if (ch == ',') |
| { |
| if (!escaped) |
| { |
| // unescaped comma, this is a string delimiter... |
| strings.add(sb.toString()); |
| sb.setLength(0); |
| start = true; |
| spaceCount = 0; |
| continue; |
| } |
| } |
| else if (ch == ' ') |
| { |
| // space is only ignored at beginning and end but not if escaped |
| if (!escaped) |
| { |
| spaceCount++; |
| continue; |
| } |
| } |
| else if (isWhitespace) |
| { |
| // Other whitespaces are ignored... |
| continue; |
| } |
| |
| if (spaceCount > 0) |
| { |
| for (int m = 0; m < spaceCount; m++) |
| { |
| sb.append(" "); |
| } |
| spaceCount = 0; |
| } |
| sb.append(ch); |
| escaped = false; |
| } |
| |
| // Always add the last string, as it contains everything after the last comma... |
| strings.add(sb.toString()); |
| |
| return strings.toArray(new String[strings.size()]); |
| } |
| |
| protected Comparable convertToType(final String value) |
| { |
| if (value != null && value.length() > 0) |
| { |
| try |
| { |
| switch (getType()) |
| { |
| case AttributeDefinition.BOOLEAN: |
| // Boolean is only Comparable starting with Java 5 |
| return new ComparableBoolean(value); |
| case AttributeDefinition.CHARACTER: |
| return new Character(value.charAt(0)); |
| case AttributeDefinition.BYTE: |
| return Byte.valueOf(value); |
| case AttributeDefinition.SHORT: |
| return Short.valueOf(value); |
| case AttributeDefinition.INTEGER: |
| return Integer.valueOf(value); |
| case AttributeDefinition.LONG: |
| return Long.valueOf(value); |
| case AttributeDefinition.FLOAT: |
| return Float.valueOf(value); |
| case AttributeDefinition.DOUBLE: |
| return Double.valueOf(value); |
| case AttributeDefinition.STRING: |
| case AttributeDefinition.PASSWORD: |
| default: |
| return value; |
| } |
| } |
| catch (NumberFormatException nfe) |
| { |
| Activator.log(LogService.LOG_INFO, "Cannot convert value '" + value + "'", nfe); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * @param values the defaultValue to set |
| */ |
| protected void setDefaultValue(String[] values, int cardinality) |
| { |
| if (values != null) |
| { |
| int count = 0; |
| int max = Math.min(values.length, Math.max(1, cardinality)); |
| for (int i = 0; count < max && i < values.length; i++) |
| { |
| if ("".equals(ADValidator.validate(this, values[i]))) |
| { |
| count++; |
| } |
| else |
| { |
| values[i] = null; |
| } |
| } |
| if (count == 0) |
| { |
| values = cardinality == 0 ? null : new String[0]; |
| } |
| else if (count != values.length) |
| { |
| String[] filterValues = new String[count]; |
| int index = 0; |
| for (int i = 0; index < count && i < values.length; i++) |
| { |
| if (values[i] != null) |
| { |
| filterValues[index] = values[i]; |
| index++; |
| } |
| } |
| values = filterValues; |
| } |
| } |
| this.defaultValue = values; |
| } |
| |
| private static class ComparableBoolean implements Comparable |
| { |
| private boolean value; |
| |
| ComparableBoolean(String boolValue) |
| { |
| value = Boolean.valueOf(boolValue).booleanValue(); |
| } |
| |
| @Override |
| public int compareTo(Object obj) |
| { |
| ComparableBoolean cb = (ComparableBoolean) obj; |
| return (cb.value == value ? 0 : (value ? 1 : -1)); |
| } |
| } |
| } |