blob: a726a20a060b5344d8b2c1659757218316d7a034 [file] [log] [blame]
/*
* 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));
}
}
}