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 +