/*
* Copyright 2013 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Eclipse Public License version 1.0, available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.jboss.forge.addon.ui.util;
import java.util.ArrayList;
import java.util.List;
import org.jboss.forge.addon.convert.CompositeConverter;
import org.jboss.forge.addon.convert.Converter;
import org.jboss.forge.addon.convert.ConverterFactory;
import org.jboss.forge.addon.facets.Facet;
import org.jboss.forge.addon.ui.facets.HintsFacet;
import org.jboss.forge.addon.ui.hints.InputType;
import org.jboss.forge.addon.ui.input.HasCompleter;
import org.jboss.forge.addon.ui.input.InputComponent;
import org.jboss.forge.addon.ui.input.ManyValued;
import org.jboss.forge.addon.ui.input.SelectComponent;
import org.jboss.forge.addon.ui.input.SingleValued;
import org.jboss.forge.addon.ui.input.UICompleter;
import org.jboss.forge.furnace.util.Strings;
/**
* Utilities for {@link InputComponent} objects
*
* @author <a href="mailto:ggastald@redhat.com">George Gastaldi</a>
*
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public final class InputComponents
{
public static final char DEFAULT_SHORT_NAME = ' ';
private static final String COLON = ":";
/**
* @return the {@link InputType} object associated to this {@link InputComponent}
*/
public static InputType getInputType(InputComponent<?, ?> input)
{
InputType result = InputType.DEFAULT;
for (Facet f : input.getFacets())
{
if (HintsFacet.class.isInstance(f))
{
result = ((HintsFacet) f).getInputType();
break;
}
}
// FIXME: The following code does NOT work when called from Eclipse. Could it be a bug in CLAC ?
// if (input.hasFacet(HintsFacet.class))
// {
// HintsFacet facet = input.getFacet(HintsFacet.class);
// result = facet.getInputType();
// }
return result;
}
/**
* @return the value stored in this {@link InputComponent}
*/
public static Object getValueFor(InputComponent<?, ?> component)
{
if (component instanceof SingleValued)
{
return ((SingleValued<?, Object>) component).getValue();
}
else if (component instanceof ManyValued)
{
return ((ManyValued<?, Object>) component).getValue();
}
else
{
return null;
}
}
/**
* Sets the value in the provided {@link InputComponent}, making any necessary conversions
*
* @param component
* @param value
*/
public static void setValueFor(final ConverterFactory converterFactory, final InputComponent<?, Object> component,
final Object value)
{
if (component instanceof SingleValued)
{
setSingleInputValue(converterFactory, component, value, false);
}
else if (component instanceof ManyValued)
{
setManyInputValue(converterFactory, component, value, false);
}
}
/**
* Sets the default value in the provided {@link InputComponent}, making any necessary conversions
*
* @param component
* @param value
*/
public static void setDefaultValueFor(final ConverterFactory converterFactory,
final InputComponent<?, Object> component,
final Object value)
{
if (component instanceof SingleValued)
{
setSingleInputValue(converterFactory, component, value, true);
}
else if (component instanceof ManyValued)
{
setManyInputValue(converterFactory, component, value, true);
}
}
private static void setSingleInputValue(final ConverterFactory converterFactory,
final InputComponent<?, Object> input, final Object value, boolean defaultValue)
{
final Object convertedType;
if (value != null)
{
convertedType = convertToUIInputValue(converterFactory, input, value);
}
else
{
convertedType = null;
}
if (defaultValue)
{
((SingleValued) input).setDefaultValue(convertedType);
}
else
{
((SingleValued) input).setValue(convertedType);
}
}
private static void setManyInputValue(final ConverterFactory converterFactory,
final InputComponent<?, Object> input, Object value, boolean defaultValue)
{
final Iterable<Object> convertedValues;
if (value != null)
{
List<Object> convertedValuesList = new ArrayList<Object>();
if (value instanceof Iterable)
{
for (Object itValue : (Iterable) value)
{
convertedValuesList.add(convertToUIInputValue(converterFactory, input, itValue));
}
}
else
{
convertedValuesList.add(convertToUIInputValue(converterFactory, input, value));
}
convertedValues = convertedValuesList;
}
else
{
convertedValues = null;
}
if (defaultValue)
{
((ManyValued) input).setDefaultValue(convertedValues);
}
else
{
((ManyValued) input).setValue(convertedValues);
}
}
/**
* Returns the converted value that matches the input
*
* @param input
* @param value
* @return
*/
public static Object convertToUIInputValue(final ConverterFactory converterFactory,
final InputComponent<?, Object> input, final Object value)
{
final Object result;
Class<Object> sourceType = (Class<Object>) value.getClass();
Class<Object> targetType = input.getValueType();
if (!targetType.isAssignableFrom(sourceType))
{
if (input instanceof SelectComponent)
{
SelectComponent<?, Object> selectComponent = (SelectComponent<?, Object>) input;
Iterable<Object> valueChoices = selectComponent.getValueChoices();
final Converter<Object, ?> selectConverter;
if (String.class.isAssignableFrom(sourceType))
{
selectConverter = (Converter<Object, ?>) getItemLabelConverter(converterFactory, selectComponent);
}
else
{
selectConverter = converterFactory.getConverter(targetType, sourceType);
}
Object chosenObj = null;
if (valueChoices != null)
{
for (Object valueChoice : valueChoices)
{
Object convertedObj = selectConverter.convert(valueChoice);
if (convertedObj.equals(value))
{
chosenObj = valueChoice;
break;
}
}
}
result = chosenObj;
}
else
{
Converter<String, Object> valueConverter = input.getValueConverter();
if (valueConverter != null)
{
if (value instanceof String)
{
result = valueConverter.convert((String) value);
}
else
{
Converter<Object, String> stringConverter = converterFactory.getConverter(sourceType, String.class);
CompositeConverter compositeConverter = new CompositeConverter(stringConverter, valueConverter);
result = compositeConverter.convert(value);
}
}
else
{
Converter<Object, Object> converter = converterFactory.getConverter(sourceType, targetType);
result = converter.convert(value);
}
}
}
else
{
result = value;
}
return result;
}
/**
* Returns if there is a value set for this {@link InputComponent}
*/
public static boolean hasValue(InputComponent<?, ?> input)
{
boolean ret;
Object value = InputComponents.getValueFor(input);
if (value == null)
{
ret = false;
}
else if (value instanceof String && value.toString().isEmpty())
{
ret = false;
}
else if (value instanceof Iterable && !((Iterable) value).iterator().hasNext())
{
ret = false;
}
else
{
ret = true;
}
return ret;
}
/**
* Validate if the input has a value. If not, return the error message
*
* @param input
* @return
*/
public static String validateRequired(final InputComponent<?, ?> input)
{
String requiredMessage = null;
if (input.isRequired() && !InputComponents.hasValue(input))
{
requiredMessage = input.getRequiredMessage();
if (Strings.isNullOrEmpty(requiredMessage))
{
String labelValue = input.getLabel() == null ? input.getName() : input.getLabel();
// Chop : off
if (labelValue.endsWith(COLON))
{
labelValue = labelValue.substring(0, labelValue.length() - 1);
}
requiredMessage = labelValue + " must be specified.";
}
}
return requiredMessage;
}
/**
*
* Returns the item label converter, that is
*
* @param converterFactory May be null
* @param input
* @return the item label converter of a {@link SelectComponent} or a {@link Converter} instance from the
* {@link ConverterFactory} parameter if not null
*/
public static <IMPLTYPE, VALUETYPE> Converter<VALUETYPE, String> getItemLabelConverter(
final ConverterFactory converterFactory,
final SelectComponent<IMPLTYPE, VALUETYPE> input)
{
Converter<VALUETYPE, String> converter = input.getItemLabelConverter();
if (converter == null && converterFactory != null)
{
converter = converterFactory.getConverter(input.getValueType(), String.class);
}
return converter;
}
/**
* Returns the label for this component
*
* @param input the input component
* @param addColon should a colon be added in the end of the label ?
* @return the label with a colon in the end if addColon is true
*/
public static String getLabelFor(InputComponent<?, ?> input, boolean addColon)
{
String label = input.getLabel();
// If no label was provided, use name
if (label == null)
{
label = input.getName();
}
// if a colon is required, add it
if (addColon && !label.endsWith(COLON))
{
label += COLON;
}
return label;
}
/**
* Returns the completer associated with this {@link InputComponent} or null if it is not available
*
* @param inputComponent
* @return the {@link UICompleter} associated with this {@link InputComponent} or null if not available or the
* {@link InputComponent} does not implement {@link HasCompleter}
*/
public static <VALUETYPE> UICompleter<VALUETYPE> getCompleterFor(InputComponent<?, VALUETYPE> inputComponent)
{
final UICompleter<VALUETYPE> result;
if (inputComponent instanceof HasCompleter)
{
result = ((HasCompleter) inputComponent).getCompleter();
}
else
{
result = null;
}
return result;
}
}