You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by we...@apache.org on 2020/04/23 09:53:30 UTC
[myfaces-tobago] 02/08: Tobago-1999: suggest
This is an automated email from the ASF dual-hosted git repository.
weber pushed a commit to branch TOBAGO-1999_Select2
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git
commit e29f0e8f0a91d9a42710410c2428a684eeecc47c
Author: Volker Weber <v....@inexso.de>
AuthorDate: Fri Jan 10 13:54:24 2020 +0100
Tobago-1999: suggest
---
.../component/AbstractUISelectManyBox.java | 35 ++-
.../component/AbstractUISelectOneChoice.java | 35 ++-
.../internal/component/AbstractUISuggest.java | 101 +++++++++
.../internal/component/UISelect2Component.java | 20 ++
.../internal/taglib/declaration/Select2.java | 1 +
.../internal/util/UISelect2ComponentUtil.java | 130 +++++++++++
.../tobago/model/AutoSuggestExtensionItem.java | 4 +-
.../tobago/model/UICustomItemContainer.java | 27 +++
.../tobago/renderkit/html/DataAttributes.java | 4 +
.../myfaces/tobago}/util/SelectItemUtils.java | 3 +-
.../tobago/example/demo/Select2Controller.java | 198 +++++++++++++++-
.../content/25-select/00-select2/select2.xhtml | 54 ++++-
.../standard/tag/SelectManyBoxRenderer.java | 24 +-
.../standard/tag/SelectOneChoiceRenderer.java | 23 +-
.../standard/standard/tag/SuggestRenderer.java | 95 +++++---
.../renderkit/html/util/HtmlRendererUtils.java | 14 +-
.../tobago/renderkit/util/SelectItemUtils.java | 248 +--------------------
.../standard/standard/script/tobago-select2.js | 141 +++++++++++-
18 files changed, 835 insertions(+), 322 deletions(-)
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyBox.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyBox.java
index 56c657d..f6ecc6b 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyBox.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectManyBox.java
@@ -6,19 +6,42 @@
package org.apache.myfaces.tobago.internal.component;
import org.apache.myfaces.tobago.internal.component.AbstractUISelectOneChoice.Select2Keys;
+import org.apache.myfaces.tobago.internal.util.UISelect2ComponentUtil;
+import org.apache.myfaces.tobago.util.ComponentUtils;
+import javax.faces.component.StateHelper;
import javax.faces.context.FacesContext;
-import java.util.ArrayList;
+import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
-public abstract class AbstractUISelectManyBox extends AbstractUISelectMany {
+public abstract class AbstractUISelectManyBox extends AbstractUISelectMany implements UISelect2Component {
+
+
+
+ public AbstractUISuggest getSuggest() {
+ return ComponentUtils.findDescendant(this, AbstractUISuggest.class);
+ }
@Override
- protected void validateValue(FacesContext context, Object convertedValue) {
- if (!isAllowCustom()) {
- super.validateValue(context, convertedValue);
- }
+ protected void validateValue(FacesContext facesContext, Object convertedValue) {
+ UISelect2ComponentUtil.ensureCustomItemsContainer(facesContext, this);
+ super.validateValue(facesContext, UISelect2ComponentUtil.ensureCustomValues(facesContext, this, convertedValue));
+ }
+
+ @Override
+ public Object getValue() {
+ return UISelect2ComponentUtil.ensureCustomValues(FacesContext.getCurrentInstance(), this, super.getValue());
+ }
+
+ @Override
+ public void encodeChildren(FacesContext facesContext) throws IOException {
+ UISelect2ComponentUtil.ensureCustomItemsContainer(facesContext, this);
+ super.encodeChildren(facesContext);
+ }
+
+ public StateHelper getComponentStateHelper() {
+ return getStateHelper();
}
public boolean isAllowClear() {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java
index 8555b95..771d2e0 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISelectOneChoice.java
@@ -19,7 +19,19 @@
package org.apache.myfaces.tobago.internal.component;
-public abstract class AbstractUISelectOneChoice extends AbstractUISelectOneBase {
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.myfaces.tobago.internal.util.UISelect2ComponentUtil;
+import org.apache.myfaces.tobago.util.ComponentUtils;
+
+import javax.faces.component.StateHelper;
+import javax.faces.context.FacesContext;
+import java.io.IOException;
+
+public abstract class AbstractUISelectOneChoice extends AbstractUISelectOneBase implements UISelect2Component {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractUISelectOneChoice.class);
enum Select2Keys {
allowClear,
@@ -36,9 +48,30 @@ public abstract class AbstractUISelectOneChoice extends AbstractUISelectOneBase
tokenSeparators
}
+ public AbstractUISuggest getSuggest() {
+ return ComponentUtils.findDescendant(this, AbstractUISuggest.class);
+ }
+
+ @Override
+ protected void validateValue(FacesContext facesContext, Object value) {
+ UISelect2ComponentUtil.ensureCustomItemsContainer(facesContext, this);
+ super.validateValue(facesContext, UISelect2ComponentUtil.ensureCustomValue(facesContext, this, value));
+ }
+ @Override
+ public Object getValue() {
+ return UISelect2ComponentUtil.ensureCustomValue(FacesContext.getCurrentInstance(), this, super.getValue());
+ }
+ @Override
+ public void encodeChildren(FacesContext facesContext) throws IOException {
+ UISelect2ComponentUtil.ensureCustomItemsContainer(facesContext, this);
+ super.encodeChildren(facesContext);
+ }
+ public StateHelper getComponentStateHelper() {
+ return getStateHelper();
+ }
public boolean isAllowClear() {
Boolean allowClear = (Boolean) getStateHelper().eval(Select2Keys.allowClear);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISuggest.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISuggest.java
index 548dda8..2197709 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISuggest.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUISuggest.java
@@ -19,26 +19,127 @@
package org.apache.myfaces.tobago.internal.component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import org.apache.myfaces.tobago.component.InputSuggest2;
import org.apache.myfaces.tobago.component.SupportsMarkup;
+import org.apache.myfaces.tobago.component.UISelectItems;
+import org.apache.myfaces.tobago.component.UISelectManyBox;
+import org.apache.myfaces.tobago.component.UISelectOneChoice;
+import org.apache.myfaces.tobago.model.AutoSuggestItem;
+import org.apache.myfaces.tobago.model.AutoSuggestItems;
import org.apache.myfaces.tobago.model.SuggestFilter;
+import javax.el.MethodExpression;
import javax.faces.component.UIComponentBase;
+import javax.faces.component.UIInput;
+import javax.faces.context.FacesContext;
+import javax.faces.convert.Converter;
+import javax.faces.model.SelectItem;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
public abstract class AbstractUISuggest
extends UIComponentBase implements SupportsMarkup, InputSuggest2 {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractUISuggest.class);
+
public static final String COMPONENT_TYPE = "org.apache.myfaces.tobago.Suggest";
public static final String COMPONENT_FAMILY = "org.apache.myfaces.tobago.Suggest";
+ private static final String ITEM_MAP_KEY
+ = "org.apache.myfaces.tobago.internal.component.AbstractUISuggest.ITEM_MAP_KEY";
+
@Override
public String getFamily() {
return COMPONENT_FAMILY;
}
+ public AutoSuggestItems getSuggestItems(FacesContext facesContext) {
+ final MethodExpression suggestMethodExpression = getSuggestMethodExpression();
+
+ UIInput in;
+ if (isSelect2()) {
+ String search = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap()
+ .get(getClientId());
+ LOG.trace(" search = \"{}\"", search);
+ in = new UIInput();
+ in.setSubmittedValue(search);
+ } else {
+ in = (UIInput) getParent();
+ }
+ AutoSuggestItems suggestItems
+ = createAutoSuggestItems(suggestMethodExpression.invoke(facesContext.getELContext(), new Object[]{in}));
+ if (!suggestItems.getItems().isEmpty()) {
+ //noinspection unchecked
+ Map<String, AutoSuggestItem> itemMap = (Map<String, AutoSuggestItem>) getStateHelper().get(ITEM_MAP_KEY);
+ if (itemMap == null) {
+ itemMap = new HashMap<String, AutoSuggestItem>();
+ getStateHelper().put(ITEM_MAP_KEY, itemMap);
+ }
+ for (AutoSuggestItem item : suggestItems.getItems()) {
+ if (!itemMap.containsKey(item.getValue())) {
+ itemMap.put(item.getValue(), item);
+ }
+ }
+ }
+ return suggestItems;
+ }
+
+ public boolean isSelect2() {
+ return getParent() instanceof UISelectManyBox || getParent() instanceof UISelectOneChoice;
+ }
+
+ private AutoSuggestItems createAutoSuggestItems(final Object object) {
+ if (object instanceof AutoSuggestItems) {
+ return (AutoSuggestItems) object;
+ }
+ final AutoSuggestItems autoSuggestItems = new AutoSuggestItems();
+ if (object instanceof List && !((List) object).isEmpty()) {
+ if (((List) object).get(0) instanceof AutoSuggestItem) {
+ //noinspection unchecked
+ autoSuggestItems.setItems((List<AutoSuggestItem>) object);
+ } else if (((List) object).get(0) instanceof String) {
+ final List<AutoSuggestItem> items = new ArrayList<AutoSuggestItem>(((List) object).size());
+ for (int i = 0; i < ((List) object).size(); i++) {
+ final AutoSuggestItem item = new AutoSuggestItem();
+ item.setLabel((String) ((List) object).get(i));
+ item.setValue((String) ((List) object).get(i));
+ items.add(item);
+ }
+ autoSuggestItems.setItems(items);
+ } else {
+ throw new ClassCastException("Can't create AutoSuggestItems from '" + object + "'. "
+ + "Elements needs to be " + String.class.getName() + " or " + AutoSuggestItem.class.getName());
+ }
+ } else {
+ autoSuggestItems.setItems(Collections.<AutoSuggestItem>emptyList());
+ }
+ return autoSuggestItems;
+ }
+
+ public SelectItem getSelectItem(FacesContext facesContext, Object value, Converter converter) {
+ //noinspection unchecked
+ Map<String, AutoSuggestItem> itemMap = (Map<String, AutoSuggestItem>) getStateHelper().get(ITEM_MAP_KEY);
+ if (itemMap != null) {
+ String id = converter != null ? converter.getAsString(facesContext, getParent(), value) : (String) value;
+ AutoSuggestItem autoSuggestItem = itemMap.get(id);
+ if (autoSuggestItem != null) {
+ return new SelectItem(value, autoSuggestItem.getLabel());
+ }
+ }
+ return null;
+ }
+
public abstract void setDelay(Integer delay);
public abstract void setMinimumCharacters(Integer minimumCharacters);
public abstract void setFilter(SuggestFilter filter);
+
+ public abstract Integer getMinimumCharacters();
}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/UISelect2Component.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/UISelect2Component.java
new file mode 100644
index 0000000..f7d456a
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/UISelect2Component.java
@@ -0,0 +1,20 @@
+package org.apache.myfaces.tobago.internal.component;
+
+import javax.faces.component.StateHelper;
+import javax.faces.component.UIComponent;
+import javax.faces.convert.Converter;
+import java.util.HashSet;
+import java.util.Map;
+
+public interface UISelect2Component {
+
+ AbstractUISuggest getSuggest();
+
+ boolean isAllowCustom();
+
+ Converter getConverter();
+
+ StateHelper getComponentStateHelper();
+
+
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/Select2.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/Select2.java
index b5506f6..e682f5d 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/Select2.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/declaration/Select2.java
@@ -2,6 +2,7 @@ package org.apache.myfaces.tobago.internal.taglib.declaration;
import org.apache.myfaces.tobago.apt.annotation.TagAttribute;
import org.apache.myfaces.tobago.apt.annotation.UIComponentTagAttribute;
+import org.apache.myfaces.tobago.internal.component.AbstractUISuggest;
public interface Select2 {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java
new file mode 100644
index 0000000..539f559
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/UISelect2ComponentUtil.java
@@ -0,0 +1,130 @@
+package org.apache.myfaces.tobago.internal.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.myfaces.tobago.internal.component.AbstractUISelectManyBox;
+import org.apache.myfaces.tobago.internal.component.AbstractUISuggest;
+import org.apache.myfaces.tobago.internal.component.UISelect2Component;
+import org.apache.myfaces.tobago.model.SelectItem;
+import org.apache.myfaces.tobago.model.UICustomItemContainer;
+import org.apache.myfaces.tobago.util.SelectItemUtils;
+
+import javax.faces.component.UIComponent;
+import javax.faces.context.FacesContext;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+public class UISelect2ComponentUtil {
+
+ private static final Logger LOG = LoggerFactory.getLogger(UISelect2ComponentUtil.class);
+
+ private static final String VALID_CUSTOMITEM_MAP
+ = "org.apache.myfaces.tobago.internal.util.UISelect2ComponentUtil.valid_CustomItem_Map";
+
+ private UISelect2ComponentUtil() {
+ }
+
+ public static Object ensureCustomValue(FacesContext facesContext, UISelect2Component component, Object value) {
+ if (value != null && !"".equals(value)) {
+ AbstractUISuggest suggest = component.getSuggest();
+ if (component.isAllowCustom() || suggest != null) {
+ ensureCustomValue(facesContext, component, value, suggest);
+ }
+ }
+ return value;
+ }
+
+ public static Object ensureCustomValues(FacesContext facesContext, UISelect2Component component, Object values) {
+ if (values != null) {
+ AbstractUISuggest suggest = component.getSuggest();
+ if (component.isAllowCustom() || suggest != null) {
+ if (values instanceof Object[]) {
+ for (Object value : (Object[]) values) {
+ ensureCustomValue(facesContext, component, value, suggest);
+ }
+ } else if (values instanceof Collection) {
+ for (Object value : (Collection) values) {
+ ensureCustomValue(facesContext, component, value, suggest);
+ }
+ }
+ }
+ }
+ return values;
+ }
+
+ public static void ensureCustomValue(
+ FacesContext facesContext, UISelect2Component component, Object value, AbstractUISuggest suggest) {
+
+ if (!isInItemlist(facesContext, component, value)) {
+ javax.faces.model.SelectItem item = null;
+ if (suggest != null) {
+ item = suggest.getSelectItem(facesContext, value, component.getConverter());
+ }
+ if (item == null && component.isAllowCustom()) {
+ item = new SelectItem(value);
+ }
+ if (item != null) {
+ LOG.trace("ADD item = \"{}\"", item.getValue());
+ getValidCustomItemMap(component).add(item);
+ }
+ }
+ }
+
+ private static boolean isInItemlist(FacesContext facesContext, UISelect2Component component, Object value) {
+ LOG.trace("check for value = \"{}\"", value);
+ Iterable<javax.faces.model.SelectItem> items
+ = SelectItemUtils.getItemIterator(facesContext, (UIComponent) component);
+ for (javax.faces.model.SelectItem item : items) {
+ LOG.trace("check item value = \"{}\"", item.getValue());
+ if (item.getValue() != null && item.getValue().equals(value)) {
+ LOG.trace("TRUE");
+ return true;
+ }
+ }
+ LOG.trace("false");
+ return false;
+ }
+
+
+ public static Set<javax.faces.model.SelectItem> getValidCustomItemMap(UISelect2Component component) {
+ Object o = component.getComponentStateHelper().get(VALID_CUSTOMITEM_MAP);
+ //noinspection unchecked
+ HashSet<javax.faces.model.SelectItem> set = (HashSet<javax.faces.model.SelectItem>) o;
+ if (set == null) {
+ set = new HashSet<javax.faces.model.SelectItem>();
+ component.getComponentStateHelper().put(VALID_CUSTOMITEM_MAP, set);
+ }
+ return set;
+ }
+
+ public static void ensureCustomItemsContainer(FacesContext facesContext, UISelect2Component component) {
+ Set<javax.faces.model.SelectItem> validCustomItemMap = getValidCustomItemMap(component);
+ boolean done = false;
+ for (UIComponent child : ((UIComponent) component).getChildren()) {
+ if (child instanceof UICustomItemContainer) {
+ ((UICustomItemContainer) child).setValue(validCustomItemMap);
+ done = true;
+ break;
+ }
+ }
+ if (!done) {
+ ((UIComponent) component).getChildren().add(new UICustomItemContainer(validCustomItemMap));
+ }
+
+ if (component instanceof AbstractUISelectManyBox) {
+ // remove duplicates
+ Set<javax.faces.model.SelectItem> set = new HashSet<javax.faces.model.SelectItem>(validCustomItemMap);
+ validCustomItemMap.clear();
+ for (javax.faces.model.SelectItem selectItem : set) {
+ LOG.trace("cleanup check = \"{}\"", selectItem.getValue());
+ if (!isInItemlist(facesContext, component, selectItem.getValue())) {
+ LOG.trace("cleanup readd = \"{}\"", selectItem.getValue());
+ validCustomItemMap.add(selectItem);
+ }
+ }
+ }
+ }
+
+}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/model/AutoSuggestExtensionItem.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/model/AutoSuggestExtensionItem.java
index 3b22356..c6ef5a8 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/model/AutoSuggestExtensionItem.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/model/AutoSuggestExtensionItem.java
@@ -20,7 +20,9 @@
package org.apache.myfaces.tobago.model;
-public class AutoSuggestExtensionItem {
+import java.io.Serializable;
+
+public class AutoSuggestExtensionItem implements Serializable {
private String id;
private String value;
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/model/UICustomItemContainer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/model/UICustomItemContainer.java
new file mode 100644
index 0000000..813d193
--- /dev/null
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/model/UICustomItemContainer.java
@@ -0,0 +1,27 @@
+package org.apache.myfaces.tobago.model;
+
+import javax.faces.component.UISelectItems;
+import java.util.Set;
+
+public class UICustomItemContainer extends UISelectItems {
+
+ public UICustomItemContainer() {
+ }
+
+ public UICustomItemContainer(Set<javax.faces.model.SelectItem> validCustomItems) {
+ setValue(validCustomItems);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return getClass().hashCode();
+ }
+
+}
\ No newline at end of file
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java
index e4a48e4..061b686 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/DataAttributes.java
@@ -228,12 +228,16 @@ public final class DataAttributes {
public static final String SUGGEST_DELAY = "data-tobago-suggest-delay";
+ public static final String SUGGEST_ID = "data-tobago-suggest-id";
+
public static final String SUGGEST_ITEM_FOR = "data-tobago-suggest-item-for";
public static final String SUGGEST_MAX_ITEMS = "data-tobago-suggest-max-items";
public static final String SUGGEST_MIN_CHARS = "data-tobago-suggest-min-chars";
+ public static final String SUGGEST_RESPONSE_DATA = "data-tobago-suggest-response-data";
+
public static final String SUGGEST_TOTAL_COUNT = "data-tobago-suggest-total-count";
public static final String SUGGEST_UPDATE = "data-tobago-suggest-update";
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/util/SelectItemUtils.java
similarity index 99%
copy from tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java
copy to tobago-core/src/main/java/org/apache/myfaces/tobago/util/SelectItemUtils.java
index cdd4ea9..874a18a 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/util/SelectItemUtils.java
@@ -16,8 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-
-package org.apache.myfaces.tobago.renderkit.util;
+package org.apache.myfaces.tobago.util;
import org.apache.myfaces.tobago.component.Attributes;
import org.apache.myfaces.tobago.component.SupportsMarkup;
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java
index 1e81080..6be927c 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/Select2Controller.java
@@ -1,27 +1,207 @@
package org.apache.myfaces.tobago.example.demo;
-import org.apache.myfaces.tobago.renderkit.html.JsonUtils;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.myfaces.tobago.model.AutoSuggestItem;
+import org.apache.myfaces.tobago.model.SelectItem;
import javax.enterprise.context.SessionScoped;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIInput;
+import javax.faces.context.FacesContext;
+import javax.faces.convert.Converter;
+import javax.faces.convert.ConverterException;
import javax.inject.Named;
import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
@SessionScoped
@Named
public class Select2Controller implements Serializable {
- private String one2;
+ private static final Logger LOG = LoggerFactory.getLogger(Select2Controller.class);
+
+ private LocaleConverter localeConverter = new LocaleConverter();
+
+ private String one2Value;
+
+ private String one3Value;
+
+ private Locale one4Locale;
+
+ private List<Locale> many8Locales;
+
+ private List<String> many7Countries;
+
+ private List<SelectItem> items;
+
+ public Select2Controller() {
+ items = new ArrayList<SelectItem>();
+ items.add(new SelectItem("letter", "Letter"));
+ items.add(new SelectItem("phone", "Phone"));
+ items.add(new SelectItem("eMail", "eMail"));
+ items.add(new SelectItem("fax", "Fax"));
+ }
+
+ public List<SelectItem> getItems() {
+ return items;
+ }
+
+ public List<SelectItem> getLocaleItems() {
+ return items;
+ }
+
+ public Converter getLocaleConverter() {
+ return localeConverter;
+ }
+
+ public List<AutoSuggestItem> suggestLocale(final UIInput input) {
+ return localeConverter.getSuggestLocale((String) input.getSubmittedValue());
+ }
+
+ public String getOne2Value() {
+ return one2Value;
+ }
+
+ public void setOne2Value(String one2Value) {
+ this.one2Value = one2Value;
+ }
+
+ public String getOne3Value() {
+ return one3Value;
+ }
+
+ public void setOne3Value(String one3Value) {
+ this.one3Value = one3Value;
+ }
+
+ public Locale getOne4Locale() {
+ LOG.warn("get one4Locale = \"{}\"", one4Locale);
+ return one4Locale;
+ }
+
+ public void setOne4Locale(Locale one4Locale) {
+ LOG.warn("set one4Locale = \"{}\"", one4Locale);
+ this.one4Locale = one4Locale;
+ }
+
+ public SelectItem[] getOne4LocaleItem() {
+ if (one4Locale != null) {
+ Locale displayLocale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
+ return new SelectItem[] {new SelectItem(one4Locale, one4Locale.getDisplayName(displayLocale))};
+ } else {
+ return new SelectItem[0];
+ }
+ }
+
+
+ public List<String> getMany7Countries() {
+ return many7Countries;
+ }
+
+ public void setMany7Countries(List<String> many7Countries) {
+ this.many7Countries = many7Countries;
+ }
+
+ public List<SelectItem> getMany7CountryItems() {
+ if (many7Countries != null && !many7Countries.isEmpty()) {
+ List<SelectItem> items = new ArrayList<SelectItem>();
+ for (String locale : many7Countries) {
+ items.add(new SelectItem(locale));
+ }
+ return items;
+ } else {
+ return Collections.emptyList();
+ }
+ }
- public String getOne2() {
- return one2;
+ public List<Locale> getMany8Locales() {
+ LOG.warn("get many8Locales = \"{}\"", many8Locales);
+ return many8Locales;
}
- public void setOne2(String one2) {
- this.one2 = one2;
+ public void setMany8Locales(List<Locale> many8Locales) {
+ LOG.warn("set many8Locales = \"{}\"", many8Locales);
+ this.many8Locales = many8Locales;
}
- public String getCaseSensitiveMatcher() {
- return "{\"matcher\": \"Tobago.Select2.caseSensitiveMatcher\"}";
-// JsonUtils.encode()
+ public List<SelectItem> getMany8LocaleItems() {
+ if (many8Locales != null && !many8Locales.isEmpty()) {
+ Locale displayLocale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
+ List<SelectItem> items = new ArrayList<SelectItem>();
+ for (Locale locale : many8Locales) {
+ items.add(new SelectItem(locale, locale.getDisplayName(displayLocale)));
+ }
+ return items;
+ } else {
+ return Collections.emptyList();
+ }
+ }
+
+ private class LocaleConverter implements Converter {
+
+ private Map<String, Locale> localeMap;
+
+ public LocaleConverter() {
+ localeMap = new HashMap<String, Locale>();
+ for (final Locale locale : Locale.getAvailableLocales()) {
+ localeMap.put(locale.toString(), locale);
+ }
+ }
+
+ public Object getAsObject(FacesContext facesContext, UIComponent uiComponent, String value)
+ throws ConverterException {
+ if (value != null) {
+ Locale locale = localeMap.get(value);
+ if (locale != null) {
+ return locale;
+ } else {
+ Locale displayLocale = facesContext.getViewRoot().getLocale();
+ for (Locale mapLocale : localeMap.values()) {
+ if (mapLocale.getDisplayName(displayLocale).equals(value)) {
+ return mapLocale;
+ }
+ }
+ throw new ConverterException("Could not convert \"" + value + "\" to Locale");
+ }
+ } else {
+ return null;
+ }
+ }
+
+ public String getAsString(FacesContext facesContext, UIComponent uiComponent, Object o) throws ConverterException {
+ if (o != null) {
+ return ((Locale) o).toString();
+ } else {
+ return null;
+ }
+ }
+
+ public List<AutoSuggestItem> getSuggestLocale(String prefix) {
+ LOG.info("Creating items for prefix: '" + prefix + "'");
+ Locale displayLocale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
+ final List<AutoSuggestItem> result = new ArrayList<AutoSuggestItem>();
+ for (final Locale locale : localeMap.values()) {
+ if (StringUtils.startsWithIgnoreCase(locale.getDisplayName(displayLocale), prefix)) {
+ AutoSuggestItem suggestItem = new AutoSuggestItem();
+ suggestItem.setValue(locale.toString());
+ suggestItem.setLabel(locale.getDisplayName(displayLocale));
+ result.add(suggestItem);
+ }
+ if (result.size() > 100) { // this value should be greater than the value of the input control
+ break;
+ }
+ }
+ return result;
+ }
+
+
}
}
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
index 4e1edeb..d5febd3 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/25-select/00-select2/select2.xhtml
@@ -67,7 +67,7 @@
<tc:selectOneChoice id="one_2"
minimumResultsForSearch="10"
placeholder="Please select message type"
- value="#{select2Controller.one2}">
+ value="#{select2Controller.one2Value}">
<tc:selectItem itemLabel="Letter" itemValue="letter"/>
<tc:selectItem itemLabel="Phone" itemValue="phone"/>
@@ -80,12 +80,18 @@
allowCustom="true"
placeholder="Custom input allowed"
allowClear="true"
- value="">
+ value="#{select2Controller.one3Value}">
- <tc:selectItem itemLabel="Letter" itemValue="letter"/>
- <tc:selectItem itemLabel="Phone" itemValue="phone"/>
- <tc:selectItem itemLabel="eMail" itemValue="eMail"/>
- <tc:selectItem itemLabel="Fax" itemValue="fax"/>
+ <tc:selectItems value="#{select2Controller.items}"/>
+ </tc:selectOneChoice>
+
+ <tc:label value="Suggest locale" for="one_4"/>
+ <tc:selectOneChoice id="one_4"
+ placeholder="Please select a locale"
+ converter="#{select2Controller.localeConverter}"
+ value="#{select2Controller.one4Locale}">
+ <tc:selectItems value="#{select2Controller.one4LocaleItem}"/>
+ <tc:suggest suggestMethod="#{select2Controller.suggestLocale}" minimumCharacters="2" />
</tc:selectOneChoice>
</tc:panel>
@@ -189,6 +195,42 @@
</tc:selectManyBox>
</tc:panel>
+ <tc:panel>
+ <f:facet name="layout">
+ <tc:gridLayout columns="400px;1*" rows="45px"/>
+ </f:facet>
+
+ <tc:label value="Suggest countries" for="many_7"/>
+ <tc:selectManyBox id="many_7"
+ placeholder="Select countries"
+ allowCustom="true"
+ tokenSeparators=","
+ value="#{select2Controller.many7Countries}">
+
+ <tc:selectItems value="#{select2Controller.many7CountryItems}"/>
+ <tc:suggest suggestMethod="#{countries.prefixed}" minimumCharacters="2" />
+ <tc:dataAttribute name="tobago-select2-extend" value='{"resultsAdapter": "suppressMessages"}'/>
+
+
+ </tc:selectManyBox>
+
+ </tc:panel>
+
+ <tc:panel>
+ <f:facet name="layout">
+ <tc:gridLayout columns="400px;1*" rows="45px"/>
+ </f:facet>
+
+ <tc:label value="Suggest locales" for="many_8"/>
+ <tc:selectManyBox id="many_8"
+ placeholder="Select locales"
+ converter="#{select2Controller.localeConverter}"
+ value="#{select2Controller.many8Locales}">
+ <tc:selectItems value="#{select2Controller.many8LocaleItems}"/>
+ <tc:suggest suggestMethod="#{select2Controller.suggestLocale}" minimumCharacters="2" />
+ </tc:selectManyBox>
+
+ </tc:panel>
</tc:panel>
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java
index 4010fa6..3c202e8 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectManyBoxRenderer.java
@@ -19,13 +19,17 @@
package org.apache.myfaces.tobago.renderkit.html.standard.standard.tag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import org.apache.myfaces.tobago.component.UISelectManyBox;
-import org.apache.myfaces.tobago.component.UISelectManyListbox;
+import org.apache.myfaces.tobago.internal.component.AbstractUISuggest;
import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
import org.apache.myfaces.tobago.layout.Measure;
import org.apache.myfaces.tobago.renderkit.SelectManyRendererBase;
import org.apache.myfaces.tobago.renderkit.css.Classes;
import org.apache.myfaces.tobago.renderkit.css.Style;
+import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
import org.apache.myfaces.tobago.renderkit.html.Select2Options;
@@ -39,9 +43,6 @@ import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import java.io.IOException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
public class SelectManyBoxRenderer extends SelectManyRendererBase {
private static final Logger LOG = LoggerFactory.getLogger(SelectManyBoxRenderer.class);
@@ -70,15 +71,21 @@ public class SelectManyBoxRenderer extends SelectManyRendererBase {
final UISelectManyBox select = (UISelectManyBox) component;
final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
+ AbstractUISuggest suggest = (AbstractUISuggest) select.getSuggest();
final String id = select.getClientId(facesContext);
final Iterable<SelectItem> items = SelectItemUtils.getItemIterator(facesContext, select);
final boolean readonly = select.isReadonly();
- final boolean disabled = (!items.iterator().hasNext() && !select.isAllowCustom())
- || select.isDisabled() || readonly;
+ final boolean disabled = !(suggest != null || select.isAllowCustom() || items.iterator().hasNext())
+ || select.isDisabled()
+ || select.isReadonly();
final Style style = new Style(facesContext, select);
- ComponentUtils.putDataAttribute(select, "tobago-select2", Select2Options.of(select).toJson());
+ Select2Options select2Options = Select2Options.of(select);
+ if (suggest != null) {
+ select2Options.setMinimumInputLength(suggest.getMinimumCharacters());
+ }
+ ComponentUtils.putDataAttribute(select, "tobago-select2", select2Options.toJson());
final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, select);
writer.startElement(HtmlElements.DIV, select);
@@ -93,6 +100,9 @@ public class SelectManyBoxRenderer extends SelectManyRendererBase {
writer.writeAttribute(HtmlAttributes.DISABLED, disabled);
writer.writeAttribute(HtmlAttributes.READONLY, readonly);
writer.writeAttribute(HtmlAttributes.REQUIRED, select.isRequired());
+ if (suggest != null) {
+ writer.writeAttribute(DataAttributes.SUGGEST_ID, suggest.getClientId(facesContext), false);
+ }
HtmlRendererUtils.renderFocus(id, select.isFocus(), ComponentUtils.isError(select), facesContext, writer);
final Integer tabIndex = select.getTabIndex();
if (tabIndex != null) {
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
index 8c4d5d8..c1963b3 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SelectOneChoiceRenderer.java
@@ -19,23 +19,24 @@
package org.apache.myfaces.tobago.renderkit.html.standard.standard.tag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import org.apache.myfaces.tobago.component.UISelectOneChoice;
-import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
+import org.apache.myfaces.tobago.internal.component.AbstractUISuggest;
import org.apache.myfaces.tobago.layout.Measure;
-import org.apache.myfaces.tobago.renderkit.html.Select2Options;
import org.apache.myfaces.tobago.renderkit.HtmlUtils;
import org.apache.myfaces.tobago.renderkit.SelectOneRendererBase;
import org.apache.myfaces.tobago.renderkit.css.Classes;
import org.apache.myfaces.tobago.renderkit.css.Style;
+import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
-import org.apache.myfaces.tobago.renderkit.html.JsonUtils;
+import org.apache.myfaces.tobago.renderkit.html.Select2Options;
import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
import org.apache.myfaces.tobago.renderkit.util.SelectItemUtils;
import org.apache.myfaces.tobago.util.ComponentUtils;
import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
@@ -68,14 +69,20 @@ public class SelectOneChoiceRenderer extends SelectOneRendererBase {
final UISelectOneChoice select = (UISelectOneChoice) component;
final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
+ AbstractUISuggest suggest = (AbstractUISuggest) select.getSuggest();
final String id = select.getClientId(facesContext);
final Iterable<SelectItem> items = SelectItemUtils.getItemIterator(facesContext, select);
final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, select);
- final boolean disabled = !items.iterator().hasNext() || select.isDisabled() || select.isReadonly();
+ final boolean disabled = !(suggest != null || select.isAllowCustom() || items.iterator().hasNext())
+ || select.isDisabled()
+ || select.isReadonly();
final Style style = new Style(facesContext, select);
final Select2Options select2Options = Select2Options.of(select);
final boolean renderAsSelect2 = select2Options.hasAnyOption();
+ if (suggest != null) {
+ select2Options.setMinimumInputLength(suggest.getMinimumCharacters());
+ }
if (renderAsSelect2) {
String json = select2Options.toJson();
@@ -107,6 +114,10 @@ public class SelectOneChoiceRenderer extends SelectOneRendererBase {
if (onchange != null) {
writer.writeAttribute(HtmlAttributes.ONCHANGE, onchange, true);
}
+ if (suggest != null) {
+ writer.writeAttribute(DataAttributes.SUGGEST_ID, suggest.getClientId(facesContext), false);
+ }
+
HtmlRendererUtils.renderCommandFacet(select, facesContext , writer);
HtmlRendererUtils.renderFocus(id, select.isFocus(), ComponentUtils.isError(select), facesContext, writer);
if (renderAsSelect2 && select.getPlaceholder() != null && select.getPlaceholder().length() > 0) {
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java
index ebc0c16..230655f 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/standard/standard/tag/SuggestRenderer.java
@@ -19,7 +19,12 @@
package org.apache.myfaces.tobago.renderkit.html.standard.standard.tag;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import org.apache.myfaces.tobago.component.UIIn;
+import org.apache.myfaces.tobago.component.UISelectManyBox;
+import org.apache.myfaces.tobago.component.UISelectOneChoice;
import org.apache.myfaces.tobago.component.UISuggest;
import org.apache.myfaces.tobago.context.ResourceManagerUtils;
import org.apache.myfaces.tobago.model.AutoSuggestItem;
@@ -29,11 +34,13 @@ import org.apache.myfaces.tobago.renderkit.css.Classes;
import org.apache.myfaces.tobago.renderkit.html.DataAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
+import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
import org.apache.myfaces.tobago.renderkit.html.util.HtmlRendererUtils;
import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
import javax.el.MethodExpression;
import javax.faces.component.UIComponent;
+import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import java.io.IOException;
import java.util.ArrayList;
@@ -42,22 +49,33 @@ import java.util.List;
public class SuggestRenderer extends InputRendererBase {
+ private static final Logger LOG = LoggerFactory.getLogger(SuggestRenderer.class);
+
@Override
public void encodeEnd(final FacesContext facesContext, final UIComponent component) throws IOException {
final UISuggest suggest = (UISuggest) component;
final TobagoResponseWriter writer = HtmlRendererUtils.getTobagoResponseWriter(facesContext);
- final String id = suggest.getClientId(facesContext);
- final UIIn in = (UIIn) suggest.getParent();
- String inClientId = in.getClientId(facesContext);
- final MethodExpression suggestMethodExpression = suggest.getSuggestMethodExpression();
- final AutoSuggestItems items
- = createAutoSuggestItems(suggestMethodExpression.invoke(facesContext.getELContext(), new Object[]{in}));
// todo: declare unused/unsupported stuff deprecated
+ if (suggest.getParent() instanceof UIIn) {
+ writeInSuggestElements(facesContext, writer, suggest);
+ } else if (suggest.isSelect2()) {
+ writeSelect2SuggestElements(facesContext, writer, suggest);
+ } else {
+ }
+ }
+
+ public void writeInSuggestElements(FacesContext facesContext, TobagoResponseWriter writer, UISuggest suggest)
+ throws IOException {
+ final UIIn in = (UIIn) suggest.getParent();
+ final String inClientId = in.getClientId(facesContext);
+
+ final AutoSuggestItems items = suggest.getSuggestItems(facesContext);
+
writer.startElement(HtmlElements.DIV, null);
writer.writeClassAttribute(Classes.create(suggest));
- writer.writeIdAttribute(id);
+ writer.writeIdAttribute(suggest.getClientId(facesContext));
writer.writeAttribute(DataAttributes.FOR, inClientId, false);
writer.writeAttribute(DataAttributes.SUGGEST_MIN_CHARS, suggest.getMinimumCharacters());
writer.writeAttribute(DataAttributes.SUGGEST_DELAY, suggest.getDelay());
@@ -107,32 +125,47 @@ public class SuggestRenderer extends InputRendererBase {
writer.endElement(HtmlElements.DIV);
}
- private AutoSuggestItems createAutoSuggestItems(final Object object) {
- if (object instanceof AutoSuggestItems) {
- return (AutoSuggestItems) object;
+ public void writeSelect2SuggestElements(
+ FacesContext facesContext, TobagoResponseWriter writer, UISuggest suggest)
+ throws IOException {
+ final UIComponent selectManyBox = (UIComponent) suggest.getParent();
+ final String inClientId = selectManyBox.getClientId(facesContext);
+ String id = suggest.getClientId(facesContext);
+
+ final AutoSuggestItems items = suggest.getSuggestItems(facesContext);
+
+ writer.startElement(HtmlElements.INPUT, null);
+ writer.writeClassAttribute(Classes.create(suggest));
+ writer.writeAttribute(HtmlAttributes.TYPE, HtmlInputTypes.HIDDEN, false);
+ writer.writeNameAttribute(id);
+ writer.writeIdAttribute(id);
+ writer.writeAttribute(DataAttributes.FOR, inClientId, false);
+ writer.writeAttribute(DataAttributes.SUGGEST_MIN_CHARS, suggest.getMinimumCharacters());
+ writer.writeAttribute(DataAttributes.SUGGEST_DELAY, suggest.getDelay());
+ writer.writeAttribute(DataAttributes.SUGGEST_MAX_ITEMS, suggest.getMaximumItems());
+ writer.writeAttribute(DataAttributes.SUGGEST_UPDATE, Boolean.toString(suggest.isUpdate()), false);
+ int totalCount = suggest.getTotalCount();
+ if (totalCount == -1) {
+ totalCount = items.getItems().size();
}
- final AutoSuggestItems autoSuggestItems = new AutoSuggestItems();
- if (object instanceof List && !((List) object).isEmpty()) {
- if (((List) object).get(0) instanceof AutoSuggestItem) {
- //noinspection unchecked
- autoSuggestItems.setItems((List<AutoSuggestItem>) object);
- } else if (((List) object).get(0) instanceof String) {
- final List<AutoSuggestItem> items = new ArrayList<AutoSuggestItem>(((List) object).size());
- for (int i = 0; i < ((List) object).size(); i++) {
- final AutoSuggestItem item = new AutoSuggestItem();
- item.setLabel((String) ((List) object).get(i));
- item.setValue((String) ((List) object).get(i));
- items.add(item);
- }
- autoSuggestItems.setItems(items);
- } else {
- throw new ClassCastException("Can't create AutoSuggestItems from '" + object + "'. "
- + "Elements needs to be " + String.class.getName() + " or " + AutoSuggestItem.class.getName());
- }
- } else {
- autoSuggestItems.setItems(Collections.<AutoSuggestItem>emptyList());
+ writer.writeAttribute(DataAttributes.SUGGEST_TOTAL_COUNT, totalCount);
+
+ StringBuilder builder = new StringBuilder("{\"results\":[");
+ for (final AutoSuggestItem item : items.getItems()) {
+ builder.append("{");
+
+ builder.append("\"id\":\"").append(item.getValue()).append("\",");
+ builder.append("\"text\":\"").append(item.getLabel()).append("\"");
+
+ builder.append("},");
+ }
+ if (builder.toString().endsWith(",")) {
+ builder.setLength(builder.length() - 1);
}
- return autoSuggestItems;
+ builder.append("]}");
+ writer.writeAttribute(DataAttributes.SUGGEST_RESPONSE_DATA, builder.toString(), true);
+
+ writer.endElement(HtmlElements.INPUT);
}
}
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/util/HtmlRendererUtils.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/util/HtmlRendererUtils.java
index 826f392..3931543 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/util/HtmlRendererUtils.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/html/util/HtmlRendererUtils.java
@@ -946,11 +946,15 @@ public final class HtmlRendererUtils {
for (final Map.Entry<Object, Object> entry : dataAttributes.entrySet()) {
final Object mapKey = entry.getKey();
final String name = mapKey instanceof ValueExpression
- ? ((ValueExpression) mapKey).getValue(elContext).toString() : mapKey.toString();
- final Object mapValue = entry.getValue();
- final String value = mapValue instanceof ValueExpression
- ? ((ValueExpression) mapValue).getValue(elContext).toString() : mapValue.toString();
- writer.writeAttribute("data-" + name, value, true);
+ ? ((ValueExpression) mapKey).getValue(elContext).toString()
+ : mapKey.toString();
+ Object mapValue = entry.getValue();
+ mapValue = mapValue instanceof ValueExpression ? ((ValueExpression) mapValue).getValue(elContext) : mapValue;
+ if (mapValue == null) {
+ throw new NullPointerException("Data attribute value of " + name + " is null on component "
+ + component.getClass().getName() + " [" + component.getClientId(context) + "]");
+ }
+ writer.writeAttribute("data-" + name, mapValue.toString(), true);
}
}
}
diff --git a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java
index cdd4ea9..fd3fd46 100644
--- a/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java
+++ b/tobago-theme/tobago-theme-standard/src/main/java/org/apache/myfaces/tobago/renderkit/util/SelectItemUtils.java
@@ -19,24 +19,10 @@
package org.apache.myfaces.tobago.renderkit.util;
-import org.apache.myfaces.tobago.component.Attributes;
-import org.apache.myfaces.tobago.component.SupportsMarkup;
-import org.apache.myfaces.tobago.context.Markup;
-
-import javax.el.ValueExpression;
import javax.faces.component.UIComponent;
-import javax.faces.component.UISelectItem;
-import javax.faces.component.UISelectItems;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
-import java.lang.reflect.Array;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
/**
* Based on code from MyFaces core.
@@ -47,21 +33,7 @@ public class SelectItemUtils {
* Creates a list of SelectItems to use for rendering.
*/
public static Iterable<SelectItem> getItemIterator(final FacesContext facesContext, final UIComponent selector) {
- if (selector.getChildCount() == 0) {
- return Collections.emptyList();
- } else {
- return new Iterable<SelectItem>() {
-
- private SelectItemsIterator iterator;
-
- public Iterator<SelectItem> iterator() {
- if (iterator == null) {
- iterator = new SelectItemsIterator(facesContext, selector);
- }
- return iterator;
- }
- };
- }
+ return org.apache.myfaces.tobago.util.SelectItemUtils.getItemIterator(facesContext, selector);
}
/**
@@ -70,222 +42,6 @@ public class SelectItemUtils {
* Otherwise please use {@link #getItemIterator(javax.faces.context.FacesContext, javax.faces.component.UIComponent)}
*/
public static List<SelectItem> getItemList(final FacesContext facesContext, final UIComponent selector) {
- if (selector.getChildCount() == 0) {
- return Collections.emptyList();
- } else {
- final Iterable<SelectItem> iterator = getItemIterator(facesContext, selector);
- final List<SelectItem> result = new ArrayList<SelectItem>();
- for (SelectItem selectItem : iterator) {
- result.add(selectItem);
- }
- return result;
- }
- }
-
- private static class SelectItemsIterator implements Iterator<SelectItem> {
-
- private final FacesContext facesContext;
- private final Iterator<UIComponent> children;
- private Iterator<?> nestedItems;
- private SelectItem nextItem;
- private UISelectItems currentUISelectItems;
-
- private SelectItemsIterator(final FacesContext facesContext, final UIComponent selector) {
- this.children = selector.getChildren().iterator();
- this.facesContext = facesContext;
- }
-
- @SuppressWarnings("unchecked")
- public boolean hasNext() {
- if (nextItem != null) {
- return true;
- }
- if (nestedItems != null) {
- if (nestedItems.hasNext()) {
- return true;
- }
- nestedItems = null;
- }
-
- UIComponent child = null;
- while (children.hasNext()) {
- final UIComponent c = children.next();
- // When there is other components nested that does
- // not extends from UISelectItem or UISelectItems
- // the behavior for this iterator is just skip this
- // element(s) until an element that extends from these
- // classes are found. If there is no more elements
- // that conform this condition, just return false.
- if (c instanceof UISelectItem || c instanceof UISelectItems) {
- child = c;
- break;
- }
- }
- if (child == null) {
- return false;
- }
-
- if (child instanceof UISelectItem) {
- final UISelectItem uiSelectItem = (UISelectItem) child;
- Object item = uiSelectItem.getValue();
- if (item == null) {
- // no value attribute --> create the SelectItem out of the other attributes
- final Object itemValue = uiSelectItem.getItemValue();
- String label = uiSelectItem.getItemLabel();
- final String description = uiSelectItem.getItemDescription();
- final boolean disabled = uiSelectItem.isItemDisabled();
-// boolean escape = uiSelectItem.isItemEscaped();
-// boolean noSelectionOption = uiSelectItem.isNoSelectionOption();
- if (label == null) {
- label = itemValue.toString();
- }
- String image = null;
- Markup markup = null;
- if (uiSelectItem instanceof org.apache.myfaces.tobago.component.UISelectItem) {
- org.apache.myfaces.tobago.component.UISelectItem tobagoSelectItem
- = (org.apache.myfaces.tobago.component.UISelectItem) uiSelectItem;
- image = tobagoSelectItem.getItemImage();
- markup = tobagoSelectItem.getCurrentMarkup();
- }
- item = new org.apache.myfaces.tobago.model.SelectItem(itemValue, label, description, disabled, image, markup);
- } else if (!(item instanceof SelectItem)) {
- ValueExpression expression = uiSelectItem.getValueExpression("value");
- throw new IllegalArgumentException("ValueExpression '"
- + (expression == null ? null : expression.getExpressionString()) + "' of UISelectItem : "
- + child + " does not reference an Object of type SelectItem");
- }
- nextItem = (SelectItem) item;
- return true;
- } else { // UISelectItems
- currentUISelectItems = ((UISelectItems) child);
- final Object value = currentUISelectItems.getValue();
-
- if (value instanceof SelectItem) {
- nextItem = (SelectItem) value;
- return true;
- } else if (value != null && value.getClass().isArray()) {
- // value is any kind of array (primitive or non-primitive)
- // --> we have to use class Array to get the values
- final int length = Array.getLength(value);
- final Collection<Object> items = new ArrayList<Object>(length);
- for (int i = 0; i < length; i++) {
- items.add(Array.get(value, i));
- }
- nestedItems = items.iterator();
- return hasNext();
- } else if (value instanceof Iterable) {
- // value is Iterable --> Collection, DataModel,...
- nestedItems = ((Iterable<?>) value).iterator();
- return hasNext();
- } else if (value instanceof Map) {
- final Map<Object, Object> map = ((Map<Object, Object>) value);
- final Collection<SelectItem> items = new ArrayList<SelectItem>(map.size());
- for (Map.Entry<Object, Object> entry : map.entrySet()) {
- items.add(new org.apache.myfaces.tobago.model.SelectItem(entry.getValue(), entry.getKey().toString()));
- }
- nestedItems = items.iterator();
- return hasNext();
- }
- }
- return false;
- }
-
- public SelectItem next() {
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
- if (nextItem != null) {
- final SelectItem value = nextItem;
- nextItem = null;
- return value;
- }
- if (nestedItems != null) {
- Object item = nestedItems.next();
-
- if (!(item instanceof SelectItem)) {
- // check new params of SelectItems (since 2.0): itemValue, itemLabel, itemDescription,...
- // Note that according to the spec UISelectItems does not provide Getter and Setter
- // methods for this values, so we have to use the attribute map
- final Map<String, Object> attributeMap = currentUISelectItems.getAttributes();
-
- // write the current item into the request map under the key listed in var, if available
- boolean wroteRequestMapVarValue = false;
- Object oldRequestMapVarValue = null;
- final String var = (String) attributeMap.get(Attributes.VAR);
- if (var != null && !"".equals(var)) {
- // save the current value of the key listed in var from the request map
- oldRequestMapVarValue = facesContext.getExternalContext().getRequestMap().put(var, item);
- wroteRequestMapVarValue = true;
- }
-
- // check the itemValue attribute
- Object itemValue = attributeMap.get(Attributes.ITEM_VALUE);
- if (itemValue == null) {
- // the itemValue attribute was not provided
- // --> use the current item as the itemValue
- itemValue = item;
- }
-
- // Spec: When iterating over the select items, toString()
- // must be called on the string rendered attribute values
- Object itemLabel = attributeMap.get(Attributes.ITEM_LABEL);
- if (itemLabel == null) {
- itemLabel = itemValue.toString();
- } else {
- itemLabel = itemLabel.toString();
- }
- Object itemDescription = attributeMap.get(Attributes.ITEM_DESCRIPTION);
- if (itemDescription != null) {
- itemDescription = itemDescription.toString();
- }
- final Boolean itemDisabled = getBooleanAttribute(currentUISelectItems, Attributes.ITEM_DISABLED, false);
- final String itemImage = (String) attributeMap.get(Attributes.ITEM_IMAGE);
- final Markup markup;
- if (currentUISelectItems instanceof SupportsMarkup) {
- markup = ((SupportsMarkup) currentUISelectItems).getCurrentMarkup();
- } else {
- markup = Markup.NULL;
- }
-// TBD: should this be possible?
-// Boolean itemLabelEscaped = getBooleanAttribute(currentUISelectItems, ITEM_LABEL_ESCAPED_PROP, true);
-// TBD ?
-// Object noSelectionValue = attributeMap.get(NO_SELECTION_VALUE_PROP);
- item = new org.apache.myfaces.tobago.model.SelectItem(
- itemValue, (String) itemLabel, (String) itemDescription, itemDisabled, itemImage, markup);
-
- // remove the value with the key from var from the request map, if previously written
- if (wroteRequestMapVarValue) {
- // If there was a previous value stored with the key from var in the request map, restore it
- if (oldRequestMapVarValue != null) {
- facesContext.getExternalContext().getRequestMap().put(var, oldRequestMapVarValue);
- } else {
- facesContext.getExternalContext().getRequestMap().remove(var);
- }
- }
- }
- return (SelectItem) item;
- }
- throw new NoSuchElementException();
- }
-
- public void remove() {
- throw new UnsupportedOperationException();
- }
-
- private boolean getBooleanAttribute(
- final UIComponent component, final String attrName, final boolean defaultValue) {
- final Object value = component.getAttributes().get(attrName);
- if (value == null) {
- return defaultValue;
- } else if (value instanceof Boolean) {
- return (Boolean) value;
- } else {
- // If the value is a String, parse the boolean.
- // This makes the following code work: <tag attribute="true" />,
- // otherwise you would have to write <tag attribute="#{true}" />.
- return Boolean.valueOf(value.toString());
- }
- }
+ return org.apache.myfaces.tobago.util.SelectItemUtils.getItemList(facesContext, selector);
}
-
}
diff --git a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/tobago-select2.js b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/tobago-select2.js
index f9f4883..86b7c95 100644
--- a/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/tobago-select2.js
+++ b/tobago-theme/tobago-theme-standard/src/main/resources/org/apache/myfaces/tobago/renderkit/html/standard/standard/script/tobago-select2.js
@@ -25,10 +25,34 @@ Tobago.Select2 = {
.each( function () {
var element = jQuery(this);
- var select2Options = jQuery.extend({}, element.data("tobago-select2"), Tobago.Select2.getExtensions(element));
+ var select2Options = jQuery.extend({}, element.data("tobago-select2"));
+
+ var suggestId = element.data("tobago-suggest-id");
+ if (typeof suggestId === "string") {
+ var suggest = jQuery(Tobago.Utils.escapeClientId(suggestId));
+ if (suggest.length) {
+ select2Options.ajax = {
+ suggestId: suggestId,
+ url: "http://localhost/just/a/dummy/url",
+ transport: Tobago.Select2.transport
+ };
+ var delay = suggest.data("tobago-suggest-delay");
+ if (delay) {
+ select2Options.ajax.delay = delay;
+ }
+ } else {
+ console.error("Suggest2 ajax problem: could not find element with id " + suggestId);
+ suggestId = undefined;
+ }
+ }
if (element.hasClass("tobago-selectManyBox")) {
select2Options.containerCss = {height: element.data("tobago-style").height};
+ if (suggestId) {
+ select2Options.dropdownCssClass = undefined; // it makes no sense to hide the ajax response
+ select2Options.createTag = Tobago.Select2.createResponseTag;
+ select2Options.templateResult = Tobago.Select2.suggestTemplateResult;
+ }
}
console.info("Select2.init" + element.attr("id") + " with data: " // @DEV_ONLY
+ JSON.stringify(select2Options)); // @DEV_ONLY
@@ -40,6 +64,7 @@ Tobago.Select2 = {
console.info("select2Options.tokenizer: " + typeof eval(select2Options.tokenizer));
select2Options.tokenizer = eval(select2Options.tokenizer)
}
+
var commands = element.data("tobago-commands");
if (commands) {
@@ -70,17 +95,109 @@ Tobago.Select2 = {
}
}
+ select2Options = jQuery.extend(select2Options, Tobago.Select2.getExtensions(element));
+ console.info("Select2 select2Options " + element.attr("id") + " with data: " // @DEV_ONLY
+ + JSON.stringify(select2Options)); // @DEV_ONLY
element.select2(select2Options);
});
},
+ createResponseTag: function (params) {
+
+ var term = jQuery.trim(params.term);
+
+ if (term === '') {
+ return null;
+ }
+
+ return {
+ id: term,
+ text: term,
+ hide: "query" === params._type
+ }
+ },
+
+ suggestTemplateResult: function (result) {
+ if (result.loading || result.hide) {
+ return null;
+ }
+ return result.text;
+ },
+
+ transport: function (params, success, failure) {
+
+ if (params.data && params.data.q !== undefined) {
+ console.debug("SELECT2 tobago params.data.q : " + params.data.q); // @DEV_ONLY
+ jQuery(Tobago.Utils.escapeClientId(params.suggestId)).val(params.data.q);
+ } else {
+ console.debug("SELECT2 tobago undef "); // @DEV_ONLY
+ }
+
+ Tobago.reloadComponent(null, params.suggestId, params.suggestId, {
+ createOverlay: false,
+ suggestId : params.suggestId,
+ afterDoUpdateSuccess: function (requestOptions) {
+ var data = jQuery(Tobago.Utils.escapeClientId(requestOptions.suggestId)).data("tobago-suggest-response-data");
+ console.debug("SELECT2 tobago SUCCESS"); // @DEV_ONLY
+ var currentValues = jQuery(Tobago.Utils.escapeClientId(params.suggestId))
+ .next().children('.tobago-selectManyBox').val();
+ if (currentValues !== undefined) {
+ // remove already selected items in select many box
+ data.results = Tobago.Select2.removeSelected(data.results, currentValues);
+ }
+ success(data);
+ },
+ afterDoUpdateError: function () {
+ console.debug("SELECT2 tobago ERROR"); // @DEV_ONLY
+ failure();
+ }
+
+ });
+
+ return {
+ abort: function () {
+ console.debug("SELECT2 tobago ABORT transport"); // @DEV_ONLY
+ }
+ }
+ },
+
+ removeSelected: function (results, currentValue) {
+ if (currentValue.length === 0) {
+ return results;
+ }
+ var newData = [];
+ for (var i = 0; i < results.length; i++) {
+ var item = results[i];
+ if (!currentValue.includes(item.id)) {
+ newData.push(item);
+ }
+ }
+ return newData;
+ },
+
getExtensions: function (element) {
var extend = element.data("tobago-select2-extend");
if (extend !== undefined) {
for (var extName in extend) {
if (extend.hasOwnProperty(extName)
&& typeof extend[extName] === "string") {
- extend[extName] = Tobago.Select2.registry[extend[extName]];
+ var extensionName = extend[extName];
+ var value = Tobago.Select2.registry[extensionName];
+ if (value) {
+ extend[extName] = value;
+ } else {
+ try {
+ value = jQuery(extensionName);
+ } catch (e) {}
+ if (!(value && value.length) && extend[extName].charAt(0) !== '#') {
+ try {
+ value = jQuery(Tobago.Utils.escapeClientId(extensionName));
+ } catch (e) {}
+ }
+ if (value && value.length) {
+ extend[extName] = value;
+ }
+ }
}
}
}
@@ -141,5 +258,25 @@ Tobago.Select2.register('caseSensitiveMatcher', function (params, data) {
return null;
});
+Tobago.Select2.register('suppressMessages', (function() {
+
+ var suppressMessages = (function () {
+
+ function SuppressMessage (decorated, $element, options, dataAdapter) {
+ decorated.call(this, $element, options, dataAdapter);
+ }
+
+ SuppressMessage.prototype.displayMessage = function(decorated, params) {
+ };
+
+ return SuppressMessage;
+ })();
+
+ var Utils = jQuery.fn.select2.amd.require('select2/utils');
+ var resultAdapter = jQuery.fn.select2.amd.require('select2/results');
+ return Utils.Decorate(resultAdapter, suppressMessages);
+
+})());
+
Tobago.registerListener(Tobago.Select2.init, Tobago.Phase.DOCUMENT_READY);
Tobago.registerListener(Tobago.Select2.init, Tobago.Phase.AFTER_UPDATE);