Commit 9801f051 authored by Romain Bioteau's avatar Romain Bioteau Committed by Romain Bioteau

Pattern expression content assist (#199)

* feat(patternExpression) add content assit for groovy expressions

closes [BS-15950](https://bonitasoft.atlassian.net/browse/BS-15950)
parent e1e1f218
......@@ -581,4 +581,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
version="3.6.100.v20140519-0906"
unpack="false"/>
<plugin
id="org.bonitasoft.studio.expression.editor.pattern"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature>
/**
* Copyright (C) 2016 Bonitasoft S.A.
* Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.common.repository;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jdt.launching.JavaRuntime;
public class JavaProjectClassLoader extends ClassLoader {
private final IJavaProject javaProject;
private static final String PROTOCOL_PREFIX = "file:///";
public JavaProjectClassLoader(IJavaProject project) {
super();
if (project == null || !project.exists())
throw new IllegalArgumentException("Invalid javaProject");
this.javaProject = project;
}
@Override
public Class findClass(String className) {
try {
final String[] classPaths = JavaRuntime.computeDefaultRuntimeClassPath(javaProject);
final URL[] urls = new URL[classPaths.length];
for (int i = 0; i < classPaths.length; i++)
urls[i] = new URL(PROTOCOL_PREFIX + computeForURLClassLoader(classPaths[i]));
final ClassLoader loader = new URLClassLoader(urls);
final Class classObject = loader.loadClass(className);
return classObject;
} catch (final Exception e) {
}
return null;
}
public List<Class> findClasses(String classNamePattern) {
final List<Class> results = new ArrayList<>();
if (classNamePattern.endsWith(".java")) {
classNamePattern = classNamePattern.substring(0, classNamePattern.lastIndexOf("."));
}
// find exact matches first
findClasses(classNamePattern, results);
// and then everything else
if (!classNamePattern.endsWith("*")) {
classNamePattern += "*";
findClasses(classNamePattern, results);
}
return results;
}
public void findClasses(String classNamePattern, final List<Class> results) {
final SearchPattern pattern = SearchPattern.createPattern(classNamePattern,
IJavaSearchConstants.TYPE, IJavaSearchConstants.TYPE,
SearchPattern.R_PATTERN_MATCH);
final SearchEngine searchEngine = new SearchEngine();
final IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaProject[] { javaProject });
final SearchRequestor requestor = new SearchRequestor() {
@Override
public void acceptSearchMatch(SearchMatch match) {
IJavaElement e = (IJavaElement) match.getElement();
final String elementName = e.getElementName();
while (e != null) {
if (e instanceof IPackageFragment) {
final IPackageFragment pf = (IPackageFragment) e;
final String className = pf.getElementName() + "." + elementName;
final Class c = findClass(className);
if (c != null) {
boolean found = false;
for (final Class cr : results) {
if (cr.getName().equals(c.getName())) {
found = true;
break;
}
}
if (!found)
results.add(c);
}
}
e = e.getParent();
}
}
};
try {
searchEngine.search(
pattern,
new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() },
scope,
requestor,
null);
} catch (final CoreException e) {
}
}
public static IJavaProject[] findProject(final String className) {
final SearchPattern pattern = SearchPattern.createPattern(className,
IJavaSearchConstants.TYPE, IJavaSearchConstants.TYPE,
SearchPattern.R_EXACT_MATCH);
final List<IJavaProject> results = new ArrayList<>();
final IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
for (final IProject p : projects) {
try {
if (p.isOpen() && p.hasNature(JavaCore.NATURE_ID)) {
final IJavaProject javaProject = JavaCore.create(p);
final SearchEngine searchEngine = new SearchEngine();
final IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaProject[] { javaProject });
final SearchRequestor requestor = new SearchRequestor() {
@Override
public void acceptSearchMatch(SearchMatch match) {
IJavaElement e = (IJavaElement) match.getElement();
final String elementName = e.getElementName();
while (e != null) {
if (e instanceof IPackageFragment) {
final IPackageFragment pf = (IPackageFragment) e;
final String n = pf.getElementName() + "." + elementName;
if (className.equals(n))
results.add(javaProject);
}
e = e.getParent();
}
}
};
searchEngine.search(
pattern,
new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() },
scope,
requestor,
null);
}
} catch (final Exception e) {
}
}
return results.toArray(new IJavaProject[results.size()]);
}
private static String computeForURLClassLoader(String classpath) {
if (!classpath.endsWith("/")) {
final File file = new File(classpath);
if (file.exists() && file.isDirectory())
classpath = classpath.concat("/");
}
return classpath;
}
}
......@@ -92,22 +92,28 @@ public class TreeExplorer extends Composite implements SWTBotConstants{
@Override
public boolean select(final Viewer viewer, final Object parentElement, final Object element) {
final Object leftSelection =((IStructuredSelection) leftTree.getSelection()).getFirstElement();
final Set<Object> parents = new HashSet<Object>();
final Set<Object> parents = new HashSet<>();
final Set<String> candidates = new HashSet<>();
Object p = contentProvider.getParent(element);
if(p != null){
parents.add(p);
candidates.add(labelProvider.getText(p));
}
while (p != null) {
p = contentProvider.getParent(p);
if(p != null){
parents.add(p);
candidates.add(labelProvider.getText(p));
}
}
if(searchField.getText().isEmpty()){
return leftSelection == null || parents.contains(leftSelection);
}
final String text = labelProvider.getText(element);
return text != null && text.toLowerCase().contains(searchField.getText().toLowerCase()) && ( leftSelection == null || parents.contains(leftSelection)) ;
candidates.add(text);
return text != null &&
isCandidate(candidates, searchField.getText()) &&
(leftSelection == null || parents.contains(leftSelection));
}
});
leftTree.addSelectionChangedListener(new ISelectionChangedListener() {
......@@ -167,8 +173,17 @@ public class TreeExplorer extends Composite implements SWTBotConstants{
});
}
protected Object[] getSubtree(final Object selection) {
final Set<Object> result = new HashSet<Object>();
protected boolean isCandidate(Set<String> candidates, String text) {
for (final String c : candidates) {
if (c.toLowerCase().contains(text.toLowerCase())) {
return true;
}
}
return false;
}
protected Object[] getSubtree(final Object selection) {
final Set<Object> result = new HashSet<>();
addChildren(result,selection);
return result.toArray();
}
......
......@@ -42,5 +42,6 @@ Require-Bundle: org.eclipse.core.runtime,
org.bonitasoft.studio.scripting;bundle-version="1.0.0",
org.junit;bundle-version="4.11.0";resolution:=optional,
org.mockito;bundle-version="1.9.5";resolution:=optional,
assertj-core;bundle-version="1.5.0";resolution:=optional
assertj-core;bundle-version="1.5.0";resolution:=optional,
org.bonitasoft.studio.expression.editor.pattern;bundle-version="7.4.0"
Bundle-ActivationPolicy: lazy
......@@ -37,10 +37,10 @@ import org.bonitasoft.studio.connector.model.definition.WidgetComponent;
import org.bonitasoft.studio.connector.model.definition.util.ConnectorDefinitionSwitch;
import org.bonitasoft.studio.connector.model.i18n.DefinitionResourceProvider;
import org.bonitasoft.studio.expression.editor.filter.AvailableExpressionTypeFilter;
import org.bonitasoft.studio.expression.editor.pattern.PatternExpressionViewer;
import org.bonitasoft.studio.expression.editor.viewer.CheckBoxExpressionViewer;
import org.bonitasoft.studio.expression.editor.viewer.ExpressionCollectionViewer;
import org.bonitasoft.studio.expression.editor.viewer.ExpressionViewer;
import org.bonitasoft.studio.expression.editor.viewer.PatternExpressionViewer;
import org.bonitasoft.studio.model.connectorconfiguration.ConnectorConfiguration;
import org.bonitasoft.studio.model.expression.AbstractExpression;
import org.bonitasoft.studio.model.expression.Expression;
......@@ -72,7 +72,7 @@ public class PageComponentSwitch extends ConnectorDefinitionSwitch<Component> im
private final Composite parent;
private final java.util.List<Section> sections = new ArrayList<Section>() ;
private final java.util.List<Section> sections = new ArrayList<>() ;
protected final IWizardContainer iWizardContainer;
protected final PageComponentSwitchBuilder componentBuilder;
......
......@@ -36,13 +36,13 @@ import org.bonitasoft.studio.connector.model.definition.Text;
import org.bonitasoft.studio.connector.model.definition.TextArea;
import org.bonitasoft.studio.connector.model.i18n.DefinitionResourceProvider;
import org.bonitasoft.studio.expression.editor.filter.AvailableExpressionTypeFilter;
import org.bonitasoft.studio.expression.editor.pattern.PatternExpressionViewer;
import org.bonitasoft.studio.expression.editor.provider.IExpressionNatureProvider;
import org.bonitasoft.studio.expression.editor.viewer.CheckBoxExpressionViewer;
import org.bonitasoft.studio.expression.editor.viewer.ExpressionCollectionViewer;
import org.bonitasoft.studio.expression.editor.viewer.ExpressionViewer;
import org.bonitasoft.studio.expression.editor.viewer.GroovyOnlyExpressionViewer;
import org.bonitasoft.studio.expression.editor.viewer.IExpressionModeListener;
import org.bonitasoft.studio.expression.editor.viewer.PatternExpressionViewer;
import org.bonitasoft.studio.model.connectorconfiguration.ConnectorConfiguration;
import org.bonitasoft.studio.model.connectorconfiguration.ConnectorConfigurationPackage;
import org.bonitasoft.studio.model.connectorconfiguration.ConnectorParameter;
......@@ -368,7 +368,7 @@ public class PageComponentSwitchBuilder {
createFieldLabel(composite, SWT.TOP, object.getId(), input.isMandatory());
final PatternExpressionViewer viewer = new PatternExpressionViewer(composite, SWT.NONE);
viewer.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).hint(400, SWT.DEFAULT).create());
viewer.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, 300).create());
viewer.addFilter(connectorExpressionContentTypeFilter);
viewer.setExpressionNameResolver(new ConnectorInputNameResolver(parameter.getKey()));
final Expression exp = (Expression) parameter.getExpression();
......
......@@ -57,8 +57,9 @@ public class ContractInputCompletionProposalComputer extends GroovyCompletionPro
}
final JavaContentAssistInvocationContext javaContext = (JavaContentAssistInvocationContext) context;
GroovyCompilationUnit compilationUnit = (GroovyCompilationUnit) javaContext.getCompilationUnit();
final ContentAssistContext contentAssistContext = createContentAssistContext(
(GroovyCompilationUnit) javaContext.getCompilationUnit(),
compilationUnit,
context.getInvocationOffset(), context.getDocument());
final List<ContractInput> inputs = getContractInputs(context);
if (inputs.isEmpty()) {
......
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.bonitasoft.studio.expression.editor.pattern</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Expression Editor Plugin
Bundle-SymbolicName: org.bonitasoft.studio.expression.editor.pattern;singleton:=true
Bundle-Version: 7.4.0.qualifier
Bundle-Activator: org.bonitasoft.studio.expression.editor.pattern.ExpressionEditorPatternPlugin
Bundle-Vendor: BonitaSoft S.A.
Require-Bundle: org.bonitasoft.studio.pics,
org.bonitasoft.studio.model.edit,
org.eclipse.emf.edit.ui,
org.bonitasoft.studio.common,
org.eclipse.emf.databinding.edit,
org.eclipse.emf.workspace,
org.eclipse.jface.databinding,
org.eclipse.ui.views.properties.tabbed,
org.eclipse.jface.text,
org.eclipse.jdt.core;bundle-version="3.7.1",
org.bonitasoft.studio.common.repository;bundle-version="1.0.0",
org.codehaus.groovy.eclipse.codeassist.completion;bundle-version="2.9.2",
org.eclipse.jdt.groovy.core;bundle-version="2.9.2",
org.bonitasoft.studio.expression.editor;bundle-version="7.4.0",
org.bonitasoft.studio.groovy;bundle-version="7.4.0",
org.codehaus.groovy.eclipse.ui,
org.eclipse.ui.ide;bundle-version="3.10.1",
org.objectweb.asm;bundle-version="5.0.1",
org.codehaus.groovy,
org.codehaus.groovy.eclipse.core;bundle-version="2.9.2"
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
Export-Package: org.bonitasoft.studio.expression.editor.pattern,
org.bonitasoft.studio.expression.editor.pattern.contentAssist
output.. = bin/
bin.includes = META-INF/,\
.,\
plugin.xml
source.. = src/
<?xml version="1.0" encoding="UTF-8"?>
<?eclipse version="3.4"?>
<plugin>
<extension
point="org.codehaus.groovy.eclipse.codeassist.completion.completionProposalFilter">
<proposalFilter
name="BonitaGroovyProposalFilter"
proposalFilter="org.bonitasoft.studio.expression.editor.pattern.contentAssist.GenericProposalFilter">
<appliesTo
projectNature="org.bonitasoft.studio.common.repository.bonitaNature">
</appliesTo>
</proposalFilter>
</extension>
</plugin>
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.bonitasoft.studio.bundles</groupId>
<artifactId>plugins</artifactId>
<version>7.4.0-SNAPSHOT</version>
</parent>
<groupId>org.bonitasoft.studio.bundles.plugins</groupId>
<artifactId>org.bonitasoft.studio.expression.editor.pattern</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
......@@ -12,7 +12,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.expression.editor.viewer;
package org.bonitasoft.studio.expression.editor.pattern;
import java.util.ArrayList;
import java.util.HashMap;
......
package org.bonitasoft.studio.expression.editor.pattern;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
/**
* The activator class controls the plug-in life cycle
*/
public class ExpressionEditorPatternPlugin extends AbstractUIPlugin {
// The plug-in ID
public static final String PLUGIN_ID = "org.bonitasoft.studio.expression.editor.pattern"; //$NON-NLS-1$
// The shared instance
private static ExpressionEditorPatternPlugin plugin;
/**
* The constructor
*/
public ExpressionEditorPatternPlugin() {
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
*/
@Override
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
*/
@Override
public void stop(BundleContext context) throws Exception {
plugin = null;
super.stop(context);
}
/**
* Returns the shared instance
*
* @return the shared instance
*/
public static ExpressionEditorPatternPlugin getDefault() {
return plugin;
}
}
/**
* Copyright (C) 2016 Bonitasoft S.A.
* Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2.0 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.bonitasoft.studio.expression.editor.pattern;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.PartInitException;
public class FakeEditorPart implements IEditorPart {
private IEditorInput editorInput;
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#addPropertyListener(org.eclipse.ui.IPropertyListener)
*/
@Override
public void addPropertyListener(IPropertyListener listener) {
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
*/
@Override
public void createPartControl(Composite parent) {
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#dispose()
*/
@Override
public void dispose() {
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#getSite()
*/
@Override
public IWorkbenchPartSite getSite() {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#getTitle()
*/
@Override
public String getTitle() {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#getTitleImage()
*/
@Override
public Image getTitleImage() {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#getTitleToolTip()
*/
@Override
public String getTitleToolTip() {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#removePropertyListener(org.eclipse.ui.IPropertyListener)
*/
@Override
public void removePropertyListener(IPropertyListener listener) {
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#setFocus()
*/
@Override
public void setFocus() {
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
@Override
public Object getAdapter(Class adapter) {
return null;
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablePart#doSave(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public void doSave(IProgressMonitor monitor) {
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablePart#doSaveAs()
*/
@Override
public void doSaveAs() {
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablePart#isDirty()
*/
@Override
public boolean isDirty() {
return false;
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed()
*/
@Override
public boolean isSaveAsAllowed() {
return false;
}
/* (non-Javadoc)
* @see org.eclipse.ui.ISaveablePart#isSaveOnCloseNeeded()
*/
@Override
public boolean isSaveOnCloseNeeded() {
return false;
}
/* (non-Javadoc)
* @see org.eclipse.ui.IEditorPart#getEditorInput()
*/