/******************************************************************************* * Copyright (c) 2010-2014 SAP AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * SAP AG - initial API and implementation *******************************************************************************/ package org.eclipse.skalli.view.component; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import org.apache.commons.lang.StringUtils; import com.vaadin.data.Validator; import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.CssLayout; import com.vaadin.ui.TextField; public class MultiTextField extends CustomField { private static final long serialVersionUID = -1926189316257001272L; private static final String STYLE_LAYOUT = "multitext-layout"; //$NON-NLS-1$ private static final String STYLE_LINE_LAYOUT = "multitext-line"; //$NON-NLS-1$ private static final String STYLE_BUTTON = "multitext-btn"; //$NON-NLS-1$ private Collection<String> values; private boolean readOnly; private CssLayout layout; private List<TextFieldEntry> textFieldEntries; private String description; private String inputPrompt; private Validator validator; private int columns = -1; private int maxSize; private static class TextFieldEntry { public TextFieldEntry(TextField textField) { this.textField = textField; } TextField textField; Button removeButton; } public MultiTextField(String caption, Collection<String> values) { if (values == null) { throw new IllegalArgumentException("argument 'value' must not be null"); } setCaption(caption); this.values = values; this.maxSize = Integer.MAX_VALUE; initTextFieldEntries(values); layout = new CssLayout(); layout.setStyleName(STYLE_LAYOUT); renderTextFields(); setCompositionRoot(layout); } private void renderTextFields() { int size = textFieldEntries.size(); int last = size - 1; for (int i = 0; i <= last; ++i) { TextFieldEntry textFieldEntry = textFieldEntries.get(i); CssLayout css = new CssLayout(); css.setStyleName(STYLE_LINE_LAYOUT); textFieldEntry.textField.setReadOnly(readOnly); css.addComponent(textFieldEntry.textField); if (!readOnly) { if (size > 1) { Button b = createRemoveButton(); textFieldEntry.removeButton = b; css.addComponent(b); } if (size < maxSize && i == last) { css.addComponent(createAddButton()); } } layout.addComponent(css); } } private void initTextFieldEntries(Collection<String> values) { textFieldEntries = new ArrayList<TextFieldEntry>(); List<String> nonBlankValues = new ArrayList<String>(); if (values != null) { for (String value : values) { if (StringUtils.isNotBlank(value)) { nonBlankValues.add(value); } } } if (nonBlankValues.isEmpty()) { TextField tf = createTextField(""); //$NON-NLS-1$ textFieldEntries.add(new TextFieldEntry(tf)); } else { for (String value : nonBlankValues) { TextField tf = createTextField(value); textFieldEntries.add(new TextFieldEntry(tf)); } } } private TextField createTextField(String value) { TextField tf = new TextField(); tf.setImmediate(true); if (columns > 0) { tf.setColumns(columns); } if (description != null) { tf.setDescription(description); } if (inputPrompt != null) { tf.setInputPrompt(inputPrompt); } if (validator != null) { tf.addValidator(validator); tf.setValidationVisible(false); } if (value != null) { tf.setValue(value); } return tf; } private Button createAddButton() { Button b = new Button("Add"); b.setStyleName(Button.STYLE_LINK); b.addStyleName(STYLE_BUTTON); b.setDescription("Add another entry"); //b.setIcon(ICON_BUTTON_ADD); b.addListener(new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { TextField tf = createTextField(""); textFieldEntries.add(new TextFieldEntry(tf)); layout.removeAllComponents(); renderTextFields(); } }); return b; } private Button createRemoveButton() { Button b = new Button("Remove"); b.setStyleName(Button.STYLE_LINK); b.addStyleName(STYLE_BUTTON); b.setDescription("Remove this entry"); //b.setIcon(ICON_BUTTON_REMOVE); b.addListener(new Button.ClickListener() { @Override public void buttonClick(ClickEvent event) { Button b = event.getButton(); Iterator<TextFieldEntry> it = textFieldEntries.iterator(); while (it.hasNext()) { TextFieldEntry textFieldEntry = it.next(); if (textFieldEntry.removeButton == b) { it.remove(); break; } } layout.removeAllComponents(); renderTextFields(); } }); return b; } public void setInputPrompt(String inputPrompt) { this.inputPrompt = inputPrompt; for (TextFieldEntry textFieldEntry : textFieldEntries) { textFieldEntry.textField.setInputPrompt(inputPrompt); } } @Override public void setDescription(String description) { this.description = description; for (TextFieldEntry textFieldEntry : textFieldEntries) { textFieldEntry.textField.setDescription(description); } } public void setColumns(int columns) { this.columns = columns; for (TextFieldEntry textFieldEntry : textFieldEntries) { textFieldEntry.textField.setColumns(columns); } } public void setMaxSize(int maxSize) { this.maxSize = maxSize <= 0 ? Integer.MAX_VALUE : maxSize; requestRepaint(); } @Override public void addValidator(Validator validator) { this.validator = validator; for (TextFieldEntry textFieldEntry : textFieldEntries) { textFieldEntry.textField.addValidator(validator); textFieldEntry.textField.setValidationVisible(false); } super.addValidator(new MultipleTextFieldValidator(validator)); } private static class MultipleTextFieldValidator implements Validator { private static final long serialVersionUID = 1929837970818336522L; private Validator validator; public MultipleTextFieldValidator(Validator validator) { this.validator = validator; } @Override public boolean isValid(Object value) { if (value != null) { for (Object item : (Collection<?>) value) { String s = (String) item; if (StringUtils.isBlank(s)) { continue; } if (!validator.isValid(s)) { return false; } } } return true; } @Override public void validate(Object value) throws InvalidValueException { List<String> errors = new ArrayList<String>(); if (value != null) { for (Object item : (Collection<?>) value) { String s = (String) item; if (StringUtils.isBlank(s)) { continue; } try { validator.validate(s); } catch (InvalidValueException e) { errors.add(e.getMessage()); } } } if (!errors.isEmpty()) { StringBuilder sb = new StringBuilder(); for (String error : errors) { if (sb.length() > 0) { sb.append("<br>"); //$NON-NLS-1$ } sb.append(error); } throw new InvalidValueException(sb.toString()); } } } @Override public Object getValue() { LinkedHashSet<String> value = new LinkedHashSet<String>(textFieldEntries.size()); copyTextFieldValues(value); return value; } @Override public void setReadOnly(boolean readOnly) { this.readOnly = readOnly; layout.removeAllComponents(); renderTextFields(); } @Override public boolean isReadOnly() { return readOnly; } @Override public void commit() throws SourceException, InvalidValueException { values.clear(); copyTextFieldValues(values); } private void copyTextFieldValues(Collection<String> values) { for (TextFieldEntry textFieldEntry : textFieldEntries) { String value = (String) textFieldEntry.textField.getValue(); if (StringUtils.isNotBlank(value)) { values.add(value); } } } @Override public Class<?> getType() { return Collection.class; } }