diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/plugin.xml b/jsf/plugins/org.eclipse.jst.jsf.core/plugin.xml
index 820cd4cbc..b92ed8819 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/plugin.xml
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/plugin.xml
@@ -461,6 +461,9 @@
+
getSearchExpressions() {
+ return Collections.emptyMap();
+ }
}
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/project/facet/JSFUtils20.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/project/facet/JSFUtils20.java
index c72436329..c7c50bbd7 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/project/facet/JSFUtils20.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/project/facet/JSFUtils20.java
@@ -12,6 +12,9 @@
*******************************************************************************/
package org.eclipse.jst.jsf.core.internal.project.facet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
import org.eclipse.jst.j2ee.model.IModelProvider;
import org.eclipse.jst.jsf.core.JSFVersion;
@@ -52,4 +55,20 @@ protected String getDefaultDefaultSuffix()
{
return DEFAULT_DEFAULT_MAPPING_SUFFIX;
}
+
+ private static final Map SEARCH_EXPRESSIONS = new LinkedHashMap<>();
+ static {
+ SEARCH_EXPRESSIONS.put("@this", "current component"); //$NON-NLS-1$ //$NON-NLS-2$
+ SEARCH_EXPRESSIONS.put("@all", "all component identifiers"); //$NON-NLS-1$ //$NON-NLS-2$
+ SEARCH_EXPRESSIONS.put("@form", "closest ancestor form of current component"); //$NON-NLS-1$ //$NON-NLS-2$
+ SEARCH_EXPRESSIONS.put("@none", "no identifiers"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ @Override
+ public Map getSearchExpressions() {
+ Map result = new LinkedHashMap<>();
+ result.putAll(super.getSearchExpressions());
+ result.putAll(SEARCH_EXPRESSIONS);
+ return result;
+ }
}
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/project/facet/JSFUtils23.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/project/facet/JSFUtils23.java
index 66be15674..bc176b88e 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/project/facet/JSFUtils23.java
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/core/internal/project/facet/JSFUtils23.java
@@ -13,6 +13,8 @@
package org.eclipse.jst.jsf.core.internal.project.facet;
import java.io.PrintWriter;
+import java.util.LinkedHashMap;
+import java.util.Map;
import org.eclipse.jst.j2ee.model.IModelProvider;
import org.eclipse.jst.jsf.core.JSFVersion;
@@ -67,4 +69,22 @@ public void doVersionSpecificConfigFile(PrintWriter pw) {
QUOTE + getVersion().toString() + QUOTE + ">\n\n"); //$NON-NLS-1$
pw.write("\n"); //$NON-NLS-1$
}
+
+ private static final Map SEARCH_EXPRESSIONS = new LinkedHashMap<>();
+ static {
+ SEARCH_EXPRESSIONS.put("@composite", "closest composite component ancestor"); //$NON-NLS-1$ //$NON-NLS-2$
+ SEARCH_EXPRESSIONS.put("@namingcontainer", "closest ancestor naming container of current component"); //$NON-NLS-1$ //$NON-NLS-2$
+ SEARCH_EXPRESSIONS.put("@parent", "parent of the current component"); //$NON-NLS-1$ //$NON-NLS-2$
+ SEARCH_EXPRESSIONS.put("@previous", "previous sibling component"); //$NON-NLS-1$ //$NON-NLS-2$
+ SEARCH_EXPRESSIONS.put("@next", "next sibling component"); //$NON-NLS-1$ //$NON-NLS-2$
+ SEARCH_EXPRESSIONS.put("@root", "UIViewRoot instance of the view"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ @Override
+ public Map getSearchExpressions() {
+ Map result = new LinkedHashMap<>();
+ result.putAll(super.getSearchExpressions());
+ result.putAll(SEARCH_EXPRESSIONS);
+ return result;
+ }
}
diff --git a/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/SearchExpressionType.java b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/SearchExpressionType.java
new file mode 100644
index 000000000..fe053b4a1
--- /dev/null
+++ b/jsf/plugins/org.eclipse.jst.jsf.core/src/org/eclipse/jst/jsf/taglibprocessing/attributevalues/SearchExpressionType.java
@@ -0,0 +1,193 @@
+package org.eclipse.jst.jsf.taglibprocessing.attributevalues;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jst.jsf.context.resolver.structureddocument.IDOMContextResolver;
+import org.eclipse.jst.jsf.context.resolver.structureddocument.IStructuredDocumentContextResolverFactory;
+import org.eclipse.jst.jsf.core.internal.JSFCorePlugin;
+import org.eclipse.jst.jsf.core.internal.project.facet.JSFUtilFactory;
+import org.eclipse.jst.jsf.core.internal.project.facet.JSFUtils;
+import org.eclipse.jst.jsf.metadataprocessors.features.PossibleValue;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Node;
+
+/**
+ * */
+public class SearchExpressionType extends StringType {
+ private static final String delimiter = " "; //$NON-NLS-1$
+ private static final String[] specialCases = { "@all", "@none"}; //$NON-NLS-1$ //$NON-NLS-2$
+
+ private Map standardExpressions;
+
+ @Override
+ public List getPossibleValues() {
+ String currentAttrValue = getCurrentAttrValue();
+ if (containsSpecialCase(currentAttrValue)) {
+ return Collections.emptyList();
+ }
+ if (currentAttrValue.length() > 0 && !currentAttrValue.endsWith(delimiter)) {
+ currentAttrValue += delimiter;
+ }
+ final String currentValue = currentAttrValue;
+ List possibleValues = super.getPossibleValues();
+ possibleValues = removeAlreadySelected(possibleValues, currentAttrValue);
+ possibleValues = setDefaultFirst(possibleValues);
+ final List specialCasesList = Arrays.asList(specialCases);
+ return possibleValues.stream()
+ .peek(val -> val.setValue(specialCasesList.indexOf(val.getValue()) == -1 ?
+ currentValue + val.getValue() : val.getValue()))
+ .collect(Collectors.toList());
+ }
+
+ private boolean containsSpecialCase(String value) {
+ if (value == null) {
+ return false;
+ }
+ for (String specialCase : specialCases) {
+ if (value.indexOf(specialCase) != -1) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private List removeAlreadySelected(List proposals, String selectionString) {
+ if (selectionString == null || selectionString.trim().isEmpty()) {
+ return proposals;
+ }
+ List result = new ArrayList<>(proposals);
+ Stream.of(selectionString.split(delimiter)).map(String::trim)
+ .forEach(selected -> result.removeIf(proposal -> selected.equals(proposal.getValue())));
+ return result;
+ }
+
+ private List setDefaultFirst(List proposals) {
+ String defaultValue = super.getDefaultValue();
+ if (defaultValue == null || defaultValue.trim().isEmpty()) {
+ return proposals;
+ }
+ final String defValue = defaultValue.trim();
+ PossibleValue defaultProposal = proposals.stream().filter(proposal -> defValue.equals(proposal.getValue())).findAny().orElse(null);
+ if (defaultProposal == null) {
+ return proposals;
+ }
+ proposals.remove(defaultProposal);
+ proposals.add(0, defaultProposal);
+ return proposals;
+ }
+
+ private String getCurrentAttrValue() {
+ if (getStructuredDocumentContext() != null){
+ IDOMContextResolver resolver = IStructuredDocumentContextResolverFactory.INSTANCE.getDOMContextResolver(getStructuredDocumentContext());
+ if (resolver != null){
+ return resolver.getNode().getNodeValue();
+ }
+ }
+ return ""; //$NON-NLS-1$
+ }
+
+ @Override
+ protected List getMDPossibleValues() {
+ List result = new ArrayList<>();
+ result.addAll(getStandardExpressions().keySet());
+ result.addAll(getComponentIdsFromCurrentDocument());
+ return result;
+ }
+
+ @Override
+ protected List getMDPossibleValuesForDisplay() {
+ List result = new ArrayList<>();
+ result.addAll(getStandardExpressions().entrySet().stream()
+ .map(entry -> entry.getKey() + " (" + entry.getValue() + ")") //$NON-NLS-1$ //$NON-NLS-2$
+ .collect(Collectors.toList()));
+ result.addAll(getComponentIdsFromCurrentDocument());
+ return result;
+ }
+
+ private Map getStandardExpressions() {
+ if (this.standardExpressions == null) {
+ Map result = new LinkedHashMap<>();
+ JSFUtils jsfUtil = new JSFUtilFactory().create(getProject2());
+ if (jsfUtil != null) {
+ result.putAll(jsfUtil.getSearchExpressions());
+ }
+ standardExpressions = result;
+ }
+ return standardExpressions;
+ }
+
+ private List getComponentIdsFromCurrentDocument() {
+ List result = new ArrayList<>();
+ if (getStructuredDocumentContext() == null) {
+ return result;
+ }
+ IDOMContextResolver resolver = IStructuredDocumentContextResolverFactory.INSTANCE.getDOMContextResolver(getStructuredDocumentContext());
+ if (resolver == null) {
+ return result;
+ }
+ Node aNode = resolver.getNode();
+ if (!(aNode instanceof Attr)) {
+ return result;
+ }
+ Node previousTagNode = ((Attr) aNode).getOwnerElement();
+ Node currentTagNode = ((Attr) aNode).getOwnerElement().getParentNode();
+ while (currentTagNode != null) {
+ for (int itemIndex = 0; itemIndex < currentTagNode.getChildNodes().getLength(); itemIndex++) {
+ Node childNode = currentTagNode.getChildNodes().item(itemIndex);
+ if (childNode == previousTagNode) {
+ continue;
+ }
+ List childNodeIds = getComponentIdsFromTagWithChildNodes(childNode, null);
+ result.addAll(childNodeIds);
+ }
+ previousTagNode = currentTagNode;
+ currentTagNode = currentTagNode.getParentNode();
+ }
+ return result;
+ }
+
+ private List getComponentIdsFromTagWithChildNodes(Node tagNode, String path) {
+ List result = new ArrayList<>();
+ Optional.of(tagNode).map(Node::getAttributes).map(o -> o.getNamedItem("id")).filter(o -> o instanceof Attr) //$NON-NLS-1$
+ .map(o -> (Attr)o).map(Attr::getValue)
+ .ifPresent(value -> result.add(path != null ? path + ":" + value.trim() : value.trim())); //$NON-NLS-1$
+ String childNodePath = null;
+ if (isNamingContainer(tagNode)) {
+ String containerId = getNamingContainerName(tagNode);
+ if (containerId != null) {
+ childNodePath = ":" + getNamingContainerName(tagNode); //$NON-NLS-1$
+ } else {
+ JSFCorePlugin.log(IStatus.WARNING,
+ String.format("Markup element %s should have id, but it hasn't", tagNode.getNodeName())); //$NON-NLS-1$
+ childNodePath = ""; //$NON-NLS-1$
+ }
+ }
+ for (int itemIndex = 0; itemIndex < tagNode.getChildNodes().getLength(); itemIndex++) {
+ Node childNode = tagNode.getChildNodes().item(itemIndex);
+ List childNodeIds = getComponentIdsFromTagWithChildNodes(childNode, childNodePath);
+ if (path != null) {
+ childNodeIds = childNodeIds.stream().map(childNodeId -> path + ":" + childNodeId).toList(); //$NON-NLS-1$
+ }
+ result.addAll(childNodeIds);
+ }
+ return result;
+ }
+
+ private boolean isNamingContainer(Node tagNode) {
+ return "form".equalsIgnoreCase(tagNode.getLocalName()); //$NON-NLS-1$
+ }
+
+ private String getNamingContainerName(Node tagNode) {
+ return Optional.of(tagNode).map(Node::getAttributes).map(o -> o.getNamedItem("id")) //$NON-NLS-1$
+ .filter(o -> o instanceof Attr).map(o -> (Attr)o).map(Attr::getValue).orElse(null);
+ }
+}
diff --git a/jsf/plugins/org.eclipse.jst.jsf.standard.tagsupport/metadata/jsf_core.xml b/jsf/plugins/org.eclipse.jst.jsf.standard.tagsupport/metadata/jsf_core.xml
index 4a55d3b6c..ac1898735 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.standard.tagsupport/metadata/jsf_core.xml
+++ b/jsf/plugins/org.eclipse.jst.jsf.standard.tagsupport/metadata/jsf_core.xml
@@ -52,7 +52,9 @@
-
+
+ org.eclipse.jst.jsf.core.attributevalues.SearchExpressionType
+
@@ -91,9 +93,10 @@
-
+
+ org.eclipse.jst.jsf.core.attributevalues.SearchExpressionType
+
-
@@ -559,7 +562,9 @@
-
+
+ org.eclipse.jst.jsf.core.attributevalues.SearchExpressionType
+
@@ -602,7 +607,9 @@
-
+
+ org.eclipse.jst.jsf.core.attributevalues.SearchExpressionType
+
@@ -647,7 +654,9 @@
-
+
+ org.eclipse.jst.jsf.core.attributevalues.SearchExpressionType
+
@@ -690,7 +699,9 @@
-
+
+ org.eclipse.jst.jsf.core.attributevalues.SearchExpressionType
+
@@ -720,7 +731,9 @@
-
+
+ org.eclipse.jst.jsf.core.attributevalues.SearchExpressionType
+
@@ -742,7 +755,9 @@
-
+
+ org.eclipse.jst.jsf.core.attributevalues.SearchExpressionType
+
@@ -780,7 +795,9 @@
-
+
+ org.eclipse.jst.jsf.core.attributevalues.SearchExpressionType
+
@@ -942,7 +959,9 @@
-
+
+ org.eclipse.jst.jsf.core.attributevalues.SearchExpressionType
+
@@ -971,4 +990,4 @@
-
\ No newline at end of file
+
diff --git a/jsf/plugins/org.eclipse.jst.jsf.standard.tagsupport/metadata/jsf_html.xml b/jsf/plugins/org.eclipse.jst.jsf.standard.tagsupport/metadata/jsf_html.xml
index 684b0bc34..f68f0fddb 100644
--- a/jsf/plugins/org.eclipse.jst.jsf.standard.tagsupport/metadata/jsf_html.xml
+++ b/jsf/plugins/org.eclipse.jst.jsf.standard.tagsupport/metadata/jsf_html.xml
@@ -1437,11 +1437,11 @@
- org.eclipse.jst.jsf.core.attributevalues.StringType
+ org.eclipse.jst.jsf.core.attributevalues.SearchExpressionType
%property.category.jsf
-
\ No newline at end of file
+