diff options
5 files changed, 253 insertions, 9 deletions
diff --git a/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/AttributeMatchStrategy.java b/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/AttributeMatchStrategy.java new file mode 100644 index 00000000000..e08035635a0 --- /dev/null +++ b/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/AttributeMatchStrategy.java @@ -0,0 +1,169 @@ +/***************************************************************************** + * Copyright (c) 2013 CEA LIST. + * + * 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: + * CEA LIST - Initial API and implementation + *****************************************************************************/ +package org.eclipse.papyrus.cdo.uml.search.internal.ui.query; + +import static java.util.regex.Pattern.quote; +import static org.eclipse.papyrus.cdo.uml.search.internal.ui.query.CDOSearchQueryProvider.isString; + +import java.util.Collection; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EClass; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.papyrus.uml.search.ui.query.QueryInfo; +import org.eclipse.papyrus.views.search.results.AbstractResultEntry; +import org.eclipse.papyrus.views.search.results.AttributeMatch; +import org.eclipse.uml2.uml.NamedElement; +import org.eclipse.uml2.uml.UMLPackage; + +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; + + +/** + * This is the AttributeMatchStrategy type. Enjoy. + */ +public abstract class AttributeMatchStrategy { + + protected final Matcher matcher; + + protected AttributeMatchStrategy(QueryInfo info) { + super(); + + final String pattern = info.isRegularExpression() ? info.getQueryText() : quote(info.getQueryText()); + int flags = info.isCaseSensitive() ? 0 : Pattern.CASE_INSENSITIVE; + matcher = Pattern.compile(pattern, flags).matcher(""); //$NON-NLS-1$ + } + + public static AttributeMatchStrategy create(QueryInfo info) { + AttributeMatchStrategy result; + + if(info.isSearchAllStringAttributes()) { + result = new AnyStringAttribute(info); + } else { + result = new NameOnly(info); + } + + return result; + } + + public static AttributeMatchStrategy create(QueryInfo info, Multimap<EClass, EAttribute> attributes) { + return new SpecificAttributes(info, attributes); + } + + public abstract void apply(AbstractResultEntry parentMatch, EObject element, Collection<? super AttributeMatch> results); + + protected void match(String value, EAttribute attribute, AbstractResultEntry parentMatch, EObject element, Collection<? super AttributeMatch> results) { + matcher.reset(value); + while(matcher.find()) { + results.add(new AttributeMatch(parentMatch, matcher.start(), matcher.end() - matcher.start(), element, attribute, null)); + } + } + + // + // Nested types + // + + private static class NameOnly extends AttributeMatchStrategy { + + NameOnly(QueryInfo info) { + super(info); + } + + @Override + public void apply(AbstractResultEntry parentMatch, EObject element, Collection<? super AttributeMatch> results) { + String name = (element instanceof NamedElement) ? ((NamedElement)element).getName() : null; + + if(name != null) { + match(name, UMLPackage.Literals.NAMED_ELEMENT__NAME, parentMatch, element, results); + } + } + } + + private static class AnyStringAttribute extends AttributeMatchStrategy { + + AnyStringAttribute(QueryInfo info) { + super(info); + } + + @Override + public void apply(AbstractResultEntry parentMatch, EObject element, Collection<? super AttributeMatch> results) { + for(EAttribute next : element.eClass().getEAllAttributes()) { + if(isString(next)) { + if(!next.isMany()) { + String value = (String)element.eGet(next); + if(value != null) { + match(value, next, parentMatch, element, results); + } + } else { + // FIXME: Using list's string representation as in core Papyrus, but it doesn't make sense + List<?> value = (List<?>)element.eGet(next); + match(value.toString(), next, parentMatch, element, results); + } + } + } + } + } + + private static class SpecificAttributes extends AttributeMatchStrategy { + + private final Multimap<EClass, EAttribute> attributes; + + private final Set<EAttribute> seen = Sets.newHashSet(); + + SpecificAttributes(QueryInfo info, Multimap<EClass, EAttribute> attributes) { + super(info); + + this.attributes = attributes; + } + + @Override + public void apply(AbstractResultEntry parentMatch, EObject element, Collection<? super AttributeMatch> results) { + seen.clear(); + + apply(element.eClass(), parentMatch, element, results); + + for(EClass next : element.eClass().getEAllSuperTypes()) { + apply(next, parentMatch, element, results); + } + } + + protected void apply(EClass eclass, AbstractResultEntry parentMatch, EObject element, Collection<? super AttributeMatch> results) { + if(this.attributes.containsKey(eclass)) { + Collection<EAttribute> attributes = this.attributes.get(eclass); + if(attributes.isEmpty()) { + attributes = eclass.getEAllAttributes(); + } + + for(EAttribute next : attributes) { + // don't repeat any attributes because of inheritance + if(seen.add(next)) { + if(!next.isMany()) { + Object value = element.eGet(next); + if(value != null) { + match(value.toString(), next, parentMatch, element, results); + } + } else { + // FIXME: Using list's string representation as in core Papyrus, but it doesn't make sense + List<?> value = (List<?>)element.eGet(next); + match(value.toString(), next, parentMatch, element, results); + } + } + } + } + } + } +} diff --git a/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/CDOPapyrusQuery.java b/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/CDOPapyrusQuery.java index 9487934eae4..f7b8541313d 100644 --- a/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/CDOPapyrusQuery.java +++ b/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/CDOPapyrusQuery.java @@ -31,12 +31,16 @@ import org.eclipse.papyrus.infra.core.services.ServicesRegistry; import org.eclipse.papyrus.infra.emf.utils.ServiceUtilsForResourceSet; import org.eclipse.papyrus.uml.search.ui.query.AbstractPapyrusQuery; import org.eclipse.papyrus.uml.search.ui.results.PapyrusSearchResult; +import org.eclipse.papyrus.views.search.results.AbstractResultEntry; import org.eclipse.papyrus.views.search.results.ModelElementMatch; import org.eclipse.papyrus.views.search.scope.ScopeEntry; import org.eclipse.search.ui.ISearchResult; +import org.eclipse.search.ui.text.Match; import org.eclipse.uml2.uml.Element; import org.eclipse.uml2.uml.UMLPackage; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; @@ -53,12 +57,15 @@ public class CDOPapyrusQuery extends AbstractPapyrusQuery { private final PapyrusSearchResult searchResult = new PapyrusSearchResult(this); - public CDOPapyrusQuery(String searchText, CDOView view, CDOQuery query) { + private final AttributeMatchStrategy attributeMatchStrategy; + + public CDOPapyrusQuery(String searchText, CDOView view, CDOQuery query, AttributeMatchStrategy attributeMatchStrategy) { super(); this.searchText = searchText; this.view = view; this.query = query; + this.attributeMatchStrategy = attributeMatchStrategy; view.addListener(createViewClosedListener()); } @@ -113,8 +120,11 @@ public class CDOPapyrusQuery extends AbstractPapyrusQuery { scopeEntries.put(resource.getURI(), scopeEntry); } - // TODO: Post-process query results to determine attribute matches? - searchResult.addMatch(new ModelElementMatch(next, scopeEntry)); + AbstractResultEntry elementMatch = new ModelElementMatch(next, scopeEntry); + searchResult.addMatch(elementMatch); + + // post-process the query result to determine specific attribute matches + postProcessAttributeMatches(searchResult, elementMatch, next); } catch (Exception e) { // can get "node not found" exceptions on incompletely // deleted resources @@ -127,6 +137,15 @@ public class CDOPapyrusQuery extends AbstractPapyrusQuery { return result; } + protected void postProcessAttributeMatches(PapyrusSearchResult searchResult, AbstractResultEntry elementMatch, Element element) { + List<Match> attributeMatches = Lists.newArrayListWithExpectedSize(1); + attributeMatchStrategy.apply(elementMatch, element, attributeMatches); + + if(!attributeMatches.isEmpty()) { + searchResult.addMatches(Iterables.toArray(attributeMatches, Match.class)); + } + } + @Override public ISearchResult getSearchResult() { return searchResult; diff --git a/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/CDOSearchQueryProvider.java b/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/CDOSearchQueryProvider.java index 34500a34f11..4c7627bcaa2 100644 --- a/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/CDOSearchQueryProvider.java +++ b/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/CDOSearchQueryProvider.java @@ -83,7 +83,7 @@ public class CDOSearchQueryProvider implements IPapyrusQueryProvider { final String searchPattern = _searchPattern.getElement1(); final boolean isRegexMatch = _searchPattern.getElement2(); - return createOCLSearchQuery(queryInfo, new Function<Triplet<QueryInfo, CDOView, Collection<URI>>, CDOQuery>() { + return createOCLSearchQuery(queryInfo, AttributeMatchStrategy.create(queryInfo), new Function<Triplet<QueryInfo, CDOView, Collection<URI>>, CDOQuery>() { @Override public CDOQuery apply(Triplet<QueryInfo, CDOView, Collection<URI>> input) { @@ -126,7 +126,7 @@ public class CDOSearchQueryProvider implements IPapyrusQueryProvider { } } - return createOCLSearchQuery(queryInfo, new Function<Triplet<QueryInfo, CDOView, Collection<URI>>, CDOQuery>() { + return createOCLSearchQuery(queryInfo, AttributeMatchStrategy.create(queryInfo, attributes), new Function<Triplet<QueryInfo, CDOView, Collection<URI>>, CDOQuery>() { @Override public CDOQuery apply(Triplet<QueryInfo, CDOView, Collection<URI>> input) { @@ -154,7 +154,7 @@ public class CDOSearchQueryProvider implements IPapyrusQueryProvider { return Pair.create(searchPattern, isRegexMatch); } - protected AbstractPapyrusQuery createOCLSearchQuery(QueryInfo queryInfo, Function<Triplet<QueryInfo, CDOView, Collection<URI>>, CDOQuery> queryFunction) { + protected AbstractPapyrusQuery createOCLSearchQuery(QueryInfo queryInfo, AttributeMatchStrategy attributeMatcheStrategy, Function<Triplet<QueryInfo, CDOView, Collection<URI>>, CDOQuery> queryFunction) { IServiceRegistryTracker tracker = new DefaultServiceRegistryTracker(); Multimap<CDOView, URI> views = getViews(queryInfo.getScope()); @@ -165,7 +165,7 @@ public class CDOSearchQueryProvider implements IPapyrusQueryProvider { // parameters for the server-side OCL query handler query.setParameter("cdoImplicitRootClass", EcorePackage.Literals.EOBJECT); - AbstractPapyrusQuery searchQuery = new CDOPapyrusQuery(queryInfo.getQueryText(), view, query); + AbstractPapyrusQuery searchQuery = new CDOPapyrusQuery(queryInfo.getQueryText(), view, query, attributeMatcheStrategy); result.add(searchQuery); try { @@ -340,7 +340,7 @@ public class CDOSearchQueryProvider implements IPapyrusQueryProvider { if(isRegexMatch) { result.append(".matches(searchPattern)"); //$NON-NLS-1$ } else { - result.append(" = searchPattern"); //$NON-NLS-1$ + result.append(".indexOf(searchPattern) > 0"); //$NON-NLS-1$ } // close the exists iterator (many case) or 'and' group (scalar case) @@ -353,7 +353,7 @@ public class CDOSearchQueryProvider implements IPapyrusQueryProvider { if(isRegexMatch) { result.append(".matches(searchPattern)"); //$NON-NLS-1$ } else { - result.append(" = searchPattern"); //$NON-NLS-1$ + result.append(".indexOf(searchPattern) > 0"); //$NON-NLS-1$ } // close the exists iterator diff --git a/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/PatternUtil.java b/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/PatternUtil.java index e9e857bb730..5a7514bf70b 100644 --- a/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/PatternUtil.java +++ b/extraplugins/cdo/org.eclipse.papyrus.cdo.uml.search.ui/src/org/eclipse/papyrus/cdo/uml/search/internal/ui/query/PatternUtil.java @@ -13,6 +13,8 @@ package org.eclipse.papyrus.cdo.uml.search.internal.ui.query; import static java.util.regex.Pattern.quote; +import java.util.regex.Matcher; + /** * Utilities for working with regex patterns in CDO OCL queries. @@ -72,4 +74,38 @@ public class PatternUtil { } return result; } + + /** + * Local post-processing of element matches uses {@link Matcher#find()} to gather up all applicable attribute matches. This method converts + * the user's search criteria into an appropriate regular expression. + * + * @param searchText + * the user's search text + * @param caseSensitive + * whether the search is case-sensitive + * @param regularExpression + * whether the search tex is to be interpreted as a regular expression + * + * @return the appropriately modified string specifying the regular expression search term, never {@code null} + */ + public static String wrapForFind(String searchText, boolean caseSensitive, boolean regularExpression) { + String result; + + if(caseSensitive) { + if(!regularExpression) { + result = quote(searchText); // simple sub-string search + } else { + result = searchText; // already a regex + } + } else { + if(!regularExpression) { + // make a case-insensitive regex matching the literal text + result = String.format("(?i)%s", quote(searchText)); + } else { + result = String.format("(?i)%s", searchText); + } + } + + return result; + } } diff --git a/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/results/AttributeMatch.java b/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/results/AttributeMatch.java index 4932a486b14..0a7996d6c0b 100644 --- a/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/results/AttributeMatch.java +++ b/plugins/views/org.eclipse.papyrus.views.search/src/org/eclipse/papyrus/views/search/results/AttributeMatch.java @@ -66,6 +66,26 @@ public class AttributeMatch extends ModelMatch { }
/**
+ * Create an attribute-match in a parent result entry that already exists.
+ *
+ * @param parent
+ * the parent result entry
+ * @param offset
+ * @param lengtt
+ * @param target
+ * the element containing the value of the attribute that matches
+ * @param attribute
+ * the attribute that raised the match
+ */
+ public AttributeMatch(AbstractResultEntry parent, int offset, int length, Object target, Object attribute, Stereotype stereotype) {
+ super(offset, length, target, (ScopeEntry)parent.getElement());
+
+ this.attribute = attribute;
+ this.stereotype = stereotype;
+ this.parent = parent;
+ }
+
+ /**
*
* @see org.eclipse.papyrus.views.search.results.AbstractResultEntry#equals(java.lang.Object)
* In addition, it checks if target is the same as well.
|