/******************************************************************************* * 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.ext.impl.internal.infobox; import java.util.Collection; import java.util.Iterator; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import org.eclipse.skalli.model.Project; import org.eclipse.skalli.model.ext.commons.TaggingUtils; import org.eclipse.skalli.services.tagging.TaggingService; import org.eclipse.skalli.view.ext.ExtensionUtil; import com.vaadin.data.Property.ValueChangeEvent; import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.data.Validator; import com.vaadin.ui.AbstractSelect.NewItemHandler; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.ComboBox; import com.vaadin.ui.Component; import com.vaadin.ui.CssLayout; import com.vaadin.ui.CustomComponent; import com.vaadin.ui.Label; import com.vaadin.ui.Layout; public class TagComponent extends CustomComponent { private static final long serialVersionUID = 2926468032572843890L; private static final String STYLE_TAG_COMPONENT = "tag-component"; //$NON-NLS-1$ private final Project project; private final TaggingService taggingService; private final ExtensionUtil util; private final Layout layout; private final CssLayout tagListLayout; private final CssLayout tagComboBoxLayout; @SuppressWarnings("serial") public TagComponent(Project project, TaggingService taggingService, ExtensionUtil util) { this.project = project; this.taggingService = taggingService; this.util = util; tagListLayout = new CssLayout() { @Override protected String getCss(Component c) { if (c instanceof Label) { return "float: left; line-height: 18px; padding-right: 3px;"; } else { return "float: left"; } } }; tagComboBoxLayout = new CssLayout(); tagComboBoxLayout.setSizeFull(); layout = new CssLayout(); layout.setSizeFull(); layout.addStyleName(STYLE_TAG_COMPONENT); paintTagView(); setCompositionRoot(layout); } @SuppressWarnings({ "deprecation", "serial" }) private void paintTagView() { layout.removeAllComponents(); Set<String> tags = TaggingUtils.getTags(project); if (tags != null && tags.size() > 0) { Layout tagListLayout = new CssLayout() { @Override protected String getCss(Component c) { if (c instanceof Label) { return "float: left; line-height:18px; padding-right: 3px;"; } else { return "float: left; padding-right: 5px"; } } }; tagListLayout.setSizeFull(); Label tagLabel = new Label("Tags:"); tagLabel.setSizeUndefined(); tagListLayout.addComponent(tagLabel); for (String tag : tags) { Button tagButton = new Button(tag, new ClickListener() { @Override public void buttonClick(ClickEvent event) { String tag = event.getButton().getCaption(); util.getNavigator().navigateTagView(tag); } }); tagButton.setStyleName(Button.STYLE_LINK); tagListLayout.addComponent(tagButton); } Button editButton = new Button("(edit tags)"); if (util.getLoggedInUser() != null) { editButton.addListener(new ClickListener() { @Override public void buttonClick(ClickEvent event) { paintTagEdit(); } }); } else { editButton.setEnabled(false); editButton.setDescription("Login to tag this project"); } editButton.setStyleName(Button.STYLE_LINK); tagListLayout.addComponent(editButton); layout.addComponent(tagListLayout); } else { if (util.getLoggedInUser() != null) { Button addTagButton = new Button("(add tags)", new ClickListener() { @Override public void buttonClick(ClickEvent event) { paintTagEdit(); } }); addTagButton.setStyleName(Button.STYLE_LINK); layout.addComponent(addTagButton); } } } @SuppressWarnings({ "deprecation", "serial" }) private void paintTagEdit() { layout.removeAllComponents(); layout.setSizeFull(); paintEditTagList(); layout.addComponent(tagListLayout); paintTagComboBoxLayout(); layout.addComponent(tagComboBoxLayout); Button doneButton = new Button("done", new ClickListener() { @Override public void buttonClick(ClickEvent event) { paintTagView(); } }); doneButton.setStyle(Button.STYLE_LINK); layout.addComponent(doneButton); } @SuppressWarnings({ "deprecation", "serial" }) private void paintEditTagList() { tagListLayout.removeAllComponents(); tagListLayout.setWidth("100%"); //$NON-NLS-1$ Label tagLabel = new Label("Tags:"); tagLabel.setSizeUndefined(); tagListLayout.addComponent(tagLabel); SortedSet<String> tags = TaggingUtils.getTags(project); for (String tag : tags) { Layout l = new CssLayout() { @Override protected String getCss(Component c) { if (c instanceof Label) { return "float: left; line-height: 18px; padding-right: 3px"; //$NON-NLS-1$ } else { return "float: left; line-height: 18px; padding-right: 5px"; //$NON-NLS-1$ } } }; Label label = new Label(tag); label.setSizeUndefined(); l.addComponent(label); Button removeButton = new Button("(remove)", new ClickListener() { @Override public void buttonClick(ClickEvent event) { String tag = (String) event.getButton().getData(); TaggingUtils.removeTag(project, tag); util.persist(project); paintTagEdit(); } }); removeButton.setStyleName(Button.STYLE_LINK); removeButton.setData(tag); removeButton.setSizeUndefined(); l.addComponent(removeButton); tagListLayout.addComponent(l); } } @SuppressWarnings("serial") private void paintTagComboBoxLayout() { tagComboBoxLayout.removeAllComponents(); // add tag input field final ComboBox tagComboBox = new ComboBox(); SortedMap<String,Integer> availableTags = taggingService.getTags(Project.class); for (String tag : availableTags.keySet()) { tagComboBox.addItem(tag); } tagComboBox.setNewItemsAllowed(true); tagComboBox.setNewItemHandler(new NewItemHandler() { @Override public void addNewItem(String newItemCaption) { Collection<Validator> validators = tagComboBox.getValidators(); for (Validator val : validators) { if (!val.isValid(newItemCaption)) { Iterator<Component> it = tagComboBoxLayout.getComponentIterator(); it.next(); // this is the combobox if (!it.hasNext()) { tagComboBoxLayout.addComponent(new Label("Use lowercase letters without whitespaces only.")); } return; } } if (!tagComboBox.containsId(newItemCaption)) { TaggingUtils.addTags(project, newItemCaption); util.persist(project); paintTagEdit(); // TODO performance? } } }); tagComboBox.addListener(new ValueChangeListener() { @Override public void valueChange(ValueChangeEvent event) { if (!TaggingUtils.hasTag(project, event.getProperty().getValue().toString())) { TaggingUtils.addTags(project, event.getProperty().getValue().toString()); util.persist(project); paintTagEdit(); // TODO performance? } } }); tagComboBox.setImmediate(true); tagComboBox.setInputPrompt("Enter tag confirmed by enter"); tagComboBox.setWidth("300px"); tagComboBox.setNullSelectionAllowed(false); tagComboBox.focus(); tagComboBox.addValidator(new TagValidator()); tagComboBoxLayout.addComponent(tagComboBox); } private static class TagValidator implements Validator { private static final long serialVersionUID = -8732474379417852332L; @Override public void validate(Object value) throws InvalidValueException { if (!isValid(value)) { throw new InvalidValueException("tags can only contain lowercase characters and digits"); } } @Override public boolean isValid(Object value) { char[] chars = value.toString().toCharArray(); for (int i = 0; i < chars.length; i++) { int charType = Character.getType(chars[i]); if (!(charType == Character.LOWERCASE_LETTER || charType == Character.DECIMAL_DIGIT_NUMBER)) { return false; } } return true; } } }