| /******************************************************************************* |
| * Copyright (c) 2012 NumberFour AG |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * NumberFour AG - initial API and Implementation (Alex Panchenko) |
| *******************************************************************************/ |
| package org.eclipse.dltk.core.tests; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.UnsupportedEncodingException; |
| |
| import org.eclipse.core.resources.IContainer; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.IncrementalProjectBuilder; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.dltk.annotations.Nullable; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.IDLTKLanguageToolkit; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.IProjectFragment; |
| import org.eclipse.dltk.core.IScriptFolder; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.core.index2.search.ISearchEngine.SearchFor; |
| import org.eclipse.dltk.core.search.IDLTKSearchConstants; |
| import org.eclipse.dltk.core.search.IDLTKSearchScope; |
| import org.eclipse.dltk.core.search.SearchEngine; |
| import org.eclipse.dltk.core.search.SearchParticipant; |
| import org.eclipse.dltk.core.search.SearchPattern; |
| import org.eclipse.dltk.core.tests.ProjectSetup.Option; |
| import org.eclipse.dltk.core.tests.model.TestSearchResults; |
| import org.eclipse.dltk.internal.core.util.Util; |
| import org.eclipse.dltk.utils.ResourceUtil; |
| import org.eclipse.osgi.util.NLS; |
| import org.junit.Assert; |
| import org.junit.rules.ExternalResource; |
| |
| public abstract class AbstractProjectSetup extends ExternalResource { |
| |
| protected boolean isVerbose() { |
| return false; |
| } |
| |
| public abstract IProject get(); |
| |
| public abstract String getProjectName(); |
| |
| /** |
| * Returns this project as {@link IScriptProject} |
| */ |
| public IScriptProject getScriptProject() { |
| return DLTKCore.create(get()); |
| } |
| |
| /** |
| * Returns the specified folder from this project. |
| */ |
| public IFolder getFolder(String name) { |
| return get().getFolder(name); |
| } |
| |
| /** |
| * Returns the specified file from this project. |
| */ |
| public IFile getFile(String name) { |
| return get().getFile(name); |
| } |
| |
| /** |
| * Returns the specified file from this project. |
| */ |
| public IFile getFile(IPath path) { |
| return get().getFile(path); |
| } |
| |
| /** |
| * Returns the specified package fragment root in this project, or |
| * <code>null</code> if it does not exist. If relative, the rootPath must be |
| * specified as a project relative path. The empty path refers to the |
| * package fragment root that is the project folder iteslf. If absolute, the |
| * rootPath refers to either an external zip, or a resource internal to the |
| * workspace |
| */ |
| public IProjectFragment getProjectFragment(String fragmentPath) |
| throws ModelException { |
| final IScriptProject project = getScriptProject(); |
| if (project == null) { |
| return null; |
| } |
| final IPath path = new Path(fragmentPath); |
| if (path.isAbsolute()) { |
| final IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace() |
| .getRoot(); |
| final IResource resource = workspaceRoot.findMember(path); |
| if (resource == null) { |
| return null; |
| } |
| // resource in the workspace |
| return project.getProjectFragment(resource); |
| } else { |
| final IProjectFragment[] roots = project.getProjectFragments(); |
| if (roots == null || roots.length == 0) { |
| return null; |
| } |
| for (IProjectFragment root : roots) { |
| if (root.getUnderlyingResource().getProjectRelativePath() |
| .equals(path)) { |
| return root; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the specified script folder in this project and the given |
| * fragment, or <code>null</code> if it does not exist. The rootPath must be |
| * specified as a project relative path. The empty path refers to the |
| * default package fragment. |
| */ |
| public IScriptFolder getScriptFolder(String fragmentPath, String path) |
| throws ModelException { |
| return getScriptFolder(fragmentPath, new Path(path)); |
| } |
| |
| /** |
| * Returns the specified script folder in this project and the given |
| * fragment, or <code>null</code> if it does not exist. The rootPath must be |
| * specified as a project relative path. The empty path refers to the |
| * default package fragment. |
| */ |
| public IScriptFolder getScriptFolder(String fragmentPath, IPath path) |
| throws ModelException { |
| final IProjectFragment root = getProjectFragment(fragmentPath); |
| if (root == null) { |
| return null; |
| } |
| return root.getScriptFolder(path); |
| } |
| |
| /** |
| * Returns the source module with the specified project-related path. The |
| * module may or may not be present. |
| */ |
| public ISourceModule getSourceModule(String path) { |
| return DLTKCore.createSourceModuleFrom(getFile(path)); |
| } |
| |
| /** |
| * Returns the specified source module in this project and the given root |
| * and folder or <code>null</code> if it does not exist. |
| */ |
| public ISourceModule getSourceModule(String rootPath, IPath path) |
| throws ModelException { |
| final IScriptFolder folder = getScriptFolder(rootPath, |
| path.removeLastSegments(1)); |
| if (folder == null) { |
| return null; |
| } |
| return folder.getSourceModule(path.lastSegment()); |
| } |
| |
| /** |
| * Returns the specified source module in this project and the given root |
| * and folder or <code>null</code> if it does not exist. |
| */ |
| public ISourceModule getSourceModule(String rootPath, String path) |
| throws ModelException { |
| return getSourceModule(rootPath, new Path(path)); |
| } |
| |
| public String getFileContentsAsString(String name) throws CoreException { |
| return getFileContentsAsString(getFile(name)); |
| } |
| |
| public String getFileContentsAsString(IFile file) throws CoreException { |
| return new String(Util.getResourceContentsAsCharArray(file)); |
| } |
| |
| /** |
| * Returns workspace this project belongs to. |
| */ |
| public IWorkspace getWorkspace() { |
| return get().getWorkspace(); |
| } |
| |
| public TestSearchResults search(IModelElement element, SearchFor searchFor) |
| throws CoreException { |
| final SearchPattern pattern = SearchPattern.createPattern(element, |
| convert(searchFor)); |
| return search(pattern, createSearchScope()); |
| } |
| |
| public IDLTKSearchScope createSearchScope() { |
| return SearchEngine.createSearchScope(getScriptProject()); |
| } |
| |
| private static int convert(SearchFor searchFor) { |
| switch (searchFor) { |
| case DECLARATIONS: |
| return IDLTKSearchConstants.DECLARATIONS; |
| case REFERENCES: |
| return IDLTKSearchConstants.REFERENCES; |
| default: |
| return IDLTKSearchConstants.ALL_OCCURRENCES; |
| } |
| } |
| |
| private TestSearchResults search(SearchPattern pattern, |
| IDLTKSearchScope scope) throws CoreException { |
| Assert.assertNotNull("Pattern should not be null", pattern); |
| final TestSearchResults results = new TestSearchResults(); |
| final SearchParticipant[] participants = new SearchParticipant[] { SearchEngine |
| .getDefaultSearchParticipant() }; |
| new SearchEngine().search(pattern, participants, scope, results, null); |
| return results; |
| } |
| |
| /** |
| * @param patternString |
| * the given pattern |
| * @param searchFor |
| * determines the nature of the searched elements |
| * <ul> |
| * <li>{@link IDLTKSearchConstants#CLASS}: only look for classes</li> |
| * <li>{@link IDLTKSearchConstants#INTERFACE}: only look for |
| * interfaces</li> |
| * <li>{@link IDLTKSearchConstants#ENUM}: only look for |
| * enumeration</li> |
| * <li>{@link IDLTKSearchConstants#ANNOTATION_TYPE}: only look |
| * for annotation type</li> |
| * <li>{@link IDLTKSearchConstants#CLASS_AND_ENUM}: only look for |
| * classes and enumerations</li> |
| * <li>{@link IDLTKSearchConstants#CLASS_AND_INTERFACE}: only |
| * look for classes and interfaces</li> |
| * <li>{@link IDLTKSearchConstants#TYPE}: look for all types (ie. |
| * classes, interfaces, enum and annotation types)</li> |
| * <li>{@link IDLTKSearchConstants#FIELD}: look for fields</li> |
| * <li>{@link IDLTKSearchConstants#METHOD}: look for methods</li> |
| * <li>{@link IDLTKSearchConstants#CONSTRUCTOR}: look for |
| * constructors</li> |
| * <li>{@link IDLTKSearchConstants#PACKAGE}: look for packages</li> |
| * </ul> |
| * @param limitTo |
| * determines the nature of the expected matches |
| * <ul> |
| * <li>{@link IDLTKSearchConstants#DECLARATIONS}: will search |
| * declarations matching with the corresponding element. In case |
| * the element is a method, declarations of matching methods in |
| * subtypes will also be found, allowing to find declarations of |
| * abstract methods, etc.<br> |
| * Note that additional flags |
| * {@link IDLTKSearchConstants#IGNORE_DECLARING_TYPE} and |
| * {@link IDLTKSearchConstants#IGNORE_RETURN_TYPE} are ignored |
| * for string patterns. This is due to the fact that client may |
| * omit to define them in string pattern to have same behavior.</li> |
| * <li>{@link IDLTKSearchConstants#REFERENCES}: will search |
| * references to the given element.</li> |
| * <li>{@link IDLTKSearchConstants#ALL_OCCURRENCES}: will search |
| * for either declarations or references as specified above.</li> |
| * <li>{@link IDLTKSearchConstants#IMPLEMENTORS}: for types, will |
| * find all types which directly implement/extend a given |
| * interface. Note that types may be only classes or only |
| * interfaces if {@link IDLTKSearchConstants#CLASS } or |
| * {@link IDLTKSearchConstants#INTERFACE} is respectively used |
| * instead of {@link IDLTKSearchConstants#TYPE}.</li> |
| * </ul> |
| */ |
| public TestSearchResults search(String patternString, int searchFor, |
| int limitTo) throws CoreException { |
| return search(patternString, searchFor, limitTo, |
| IDLTKSearchConstantsForTests.EXACT_RULE); |
| } |
| |
| /** |
| * @param patternString |
| * the given pattern |
| * @param searchFor |
| * determines the nature of the searched elements |
| * <ul> |
| * <li>{@link IDLTKSearchConstants#CLASS}: only look for classes</li> |
| * <li>{@link IDLTKSearchConstants#INTERFACE}: only look for |
| * interfaces</li> |
| * <li>{@link IDLTKSearchConstants#ENUM}: only look for |
| * enumeration</li> |
| * <li>{@link IDLTKSearchConstants#ANNOTATION_TYPE}: only look |
| * for annotation type</li> |
| * <li>{@link IDLTKSearchConstants#CLASS_AND_ENUM}: only look for |
| * classes and enumerations</li> |
| * <li>{@link IDLTKSearchConstants#CLASS_AND_INTERFACE}: only |
| * look for classes and interfaces</li> |
| * <li>{@link IDLTKSearchConstants#TYPE}: look for all types (ie. |
| * classes, interfaces, enum and annotation types)</li> |
| * <li>{@link IDLTKSearchConstants#FIELD}: look for fields</li> |
| * <li>{@link IDLTKSearchConstants#METHOD}: look for methods</li> |
| * <li>{@link IDLTKSearchConstants#CONSTRUCTOR}: look for |
| * constructors</li> |
| * <li>{@link IDLTKSearchConstants#PACKAGE}: look for packages</li> |
| * </ul> |
| * @param limitTo |
| * determines the nature of the expected matches |
| * <ul> |
| * <li>{@link IDLTKSearchConstants#DECLARATIONS}: will search |
| * declarations matching with the corresponding element. In case |
| * the element is a method, declarations of matching methods in |
| * subtypes will also be found, allowing to find declarations of |
| * abstract methods, etc.<br> |
| * Note that additional flags |
| * {@link IDLTKSearchConstants#IGNORE_DECLARING_TYPE} and |
| * {@link IDLTKSearchConstants#IGNORE_RETURN_TYPE} are ignored |
| * for string patterns. This is due to the fact that client may |
| * omit to define them in string pattern to have same behavior.</li> |
| * <li>{@link IDLTKSearchConstants#REFERENCES}: will search |
| * references to the given element.</li> |
| * <li>{@link IDLTKSearchConstants#ALL_OCCURRENCES}: will search |
| * for either declarations or references as specified above.</li> |
| * <li>{@link IDLTKSearchConstants#IMPLEMENTORS}: for types, will |
| * find all types which directly implement/extend a given |
| * interface. Note that types may be only classes or only |
| * interfaces if {@link IDLTKSearchConstants#CLASS } or |
| * {@link IDLTKSearchConstants#INTERFACE} is respectively used |
| * instead of {@link IDLTKSearchConstants#TYPE}.</li> |
| * </ul> |
| * @param matchRule |
| * one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, |
| * {@link #R_PATTERN_MATCH}, {@link #R_REGEXP_MATCH}, |
| * {@link #R_CAMELCASE_MATCH} combined with one of following |
| * values: {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or |
| * {@link #R_EQUIVALENT_MATCH}. e.g. {@link #R_EXACT_MATCH} | |
| * {@link #R_CASE_SENSITIVE} if an exact and case sensitive match |
| * is requested, {@link #R_PREFIX_MATCH} if a prefix non case |
| * sensitive match is requested or {@link #R_EXACT_MATCH} | |
| * {@link #R_ERASURE_MATCH} if a non case sensitive and erasure |
| * match is requested.<br> |
| * Note that {@link #R_ERASURE_MATCH} or |
| * {@link #R_EQUIVALENT_MATCH} have no effect on non-generic |
| * types/methods search.<br> |
| * Note also that default behavior for generic types/methods |
| * search is to find exact matches. |
| */ |
| protected TestSearchResults search(String patternString, int searchFor, |
| int limitTo, int matchRule) throws CoreException { |
| final IDLTKSearchScope scope = createSearchScope(); |
| return search(patternString, searchFor, limitTo, matchRule, scope); |
| } |
| |
| /** |
| * @param patternString |
| * the given pattern |
| * @param searchFor |
| * determines the nature of the searched elements |
| * <ul> |
| * <li>{@link IDLTKSearchConstants#CLASS}: only look for classes</li> |
| * <li>{@link IDLTKSearchConstants#INTERFACE}: only look for |
| * interfaces</li> |
| * <li>{@link IDLTKSearchConstants#ENUM}: only look for |
| * enumeration</li> |
| * <li>{@link IDLTKSearchConstants#ANNOTATION_TYPE}: only look |
| * for annotation type</li> |
| * <li>{@link IDLTKSearchConstants#CLASS_AND_ENUM}: only look for |
| * classes and enumerations</li> |
| * <li>{@link IDLTKSearchConstants#CLASS_AND_INTERFACE}: only |
| * look for classes and interfaces</li> |
| * <li>{@link IDLTKSearchConstants#TYPE}: look for all types (ie. |
| * classes, interfaces, enum and annotation types)</li> |
| * <li>{@link IDLTKSearchConstants#FIELD}: look for fields</li> |
| * <li>{@link IDLTKSearchConstants#METHOD}: look for methods</li> |
| * <li>{@link IDLTKSearchConstants#CONSTRUCTOR}: look for |
| * constructors</li> |
| * <li>{@link IDLTKSearchConstants#PACKAGE}: look for packages</li> |
| * </ul> |
| * @param limitTo |
| * determines the nature of the expected matches |
| * <ul> |
| * <li>{@link IDLTKSearchConstants#DECLARATIONS}: will search |
| * declarations matching with the corresponding element. In case |
| * the element is a method, declarations of matching methods in |
| * subtypes will also be found, allowing to find declarations of |
| * abstract methods, etc.<br> |
| * Note that additional flags |
| * {@link IDLTKSearchConstants#IGNORE_DECLARING_TYPE} and |
| * {@link IDLTKSearchConstants#IGNORE_RETURN_TYPE} are ignored |
| * for string patterns. This is due to the fact that client may |
| * omit to define them in string pattern to have same behavior.</li> |
| * <li>{@link IDLTKSearchConstants#REFERENCES}: will search |
| * references to the given element.</li> |
| * <li>{@link IDLTKSearchConstants#ALL_OCCURRENCES}: will search |
| * for either declarations or references as specified above.</li> |
| * <li>{@link IDLTKSearchConstants#IMPLEMENTORS}: for types, will |
| * find all types which directly implement/extend a given |
| * interface. Note that types may be only classes or only |
| * interfaces if {@link IDLTKSearchConstants#CLASS } or |
| * {@link IDLTKSearchConstants#INTERFACE} is respectively used |
| * instead of {@link IDLTKSearchConstants#TYPE}.</li> |
| * </ul> |
| * @param matchRule |
| * one of {@link #R_EXACT_MATCH}, {@link #R_PREFIX_MATCH}, |
| * {@link #R_PATTERN_MATCH}, {@link #R_REGEXP_MATCH}, |
| * {@link #R_CAMELCASE_MATCH} combined with one of following |
| * values: {@link #R_CASE_SENSITIVE}, {@link #R_ERASURE_MATCH} or |
| * {@link #R_EQUIVALENT_MATCH}. e.g. {@link #R_EXACT_MATCH} | |
| * {@link #R_CASE_SENSITIVE} if an exact and case sensitive match |
| * is requested, {@link #R_PREFIX_MATCH} if a prefix non case |
| * sensitive match is requested or {@link #R_EXACT_MATCH} | |
| * {@link #R_ERASURE_MATCH} if a non case sensitive and erasure |
| * match is requested.<br> |
| * Note that {@link #R_ERASURE_MATCH} or |
| * {@link #R_EQUIVALENT_MATCH} have no effect on non-generic |
| * types/methods search.<br> |
| * Note also that default behavior for generic types/methods |
| * search is to find exact matches. |
| * @param scope |
| */ |
| public TestSearchResults search(String patternString, int searchFor, |
| int limitTo, int matchRule, final IDLTKSearchScope scope) |
| throws CoreException { |
| if (patternString.indexOf('*') != -1 |
| || patternString.indexOf('?') != -1) { |
| matchRule |= SearchPattern.R_PATTERN_MATCH; |
| } |
| final IDLTKLanguageToolkit toolkit = scope.getLanguageToolkit(); |
| final SearchPattern pattern = SearchPattern.createPattern( |
| patternString, searchFor, limitTo, matchRule, toolkit); |
| return search(pattern, scope); |
| } |
| |
| /** |
| * Returns all the problems of the resource with the specified name. |
| */ |
| public IMarker[] findProblems(String resourceName) throws CoreException { |
| final IResource resource = get().findMember(resourceName); |
| Assert.assertNotNull(NLS.bind("Resource {0} not found in {1}", |
| resourceName, getProjectName()), resource); |
| return ProblemTestUtil.findProblems(resource); |
| } |
| |
| /** |
| * Performs the incremental build in this project. |
| */ |
| public void build() throws CoreException { |
| final long start = System.currentTimeMillis(); |
| get().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); |
| if (isVerbose()) { |
| System.out.println((System.currentTimeMillis() - start) |
| + " ms for incremental build of " + getProjectName() |
| + " project"); |
| } |
| } |
| |
| /** |
| * Writes the specified content to the specfied content, creating the file |
| * and all the parent folders if the don't exist. |
| * |
| * @param fileName |
| * @param contents |
| * @return |
| * @throws UnsupportedEncodingException |
| * @throws CoreException |
| */ |
| public IFile writeFile(String fileName, String contents) |
| throws CoreException { |
| final IFile file = getFile(fileName); |
| final byte[] bytes; |
| try { |
| bytes = contents.getBytes(file.getCharset()); |
| } catch (UnsupportedEncodingException e) { |
| throw new IllegalStateException(e); |
| } |
| final ByteArrayInputStream input = new ByteArrayInputStream(bytes); |
| if (file.exists()) { |
| file.setContents(input, IResource.NONE, null); |
| } else { |
| final IContainer parent = file.getParent(); |
| if (parent instanceof IFolder) { |
| ResourceUtil.createFolder((IFolder) parent, null); |
| } |
| file.create(input, IResource.NONE, null); |
| } |
| return file; |
| } |
| |
| /** |
| * Deletes all the members in the specified folder. If some members were |
| * deleted and option == {@link Option#BUILD} then the build operation is |
| * also executed. |
| * |
| * @param folderName |
| * folder name |
| * @param option |
| * {@link Option#BUILD} or <code>null</code> |
| * @throws CoreException |
| */ |
| public void deleteFolderMembers(String folderName, @Nullable Option option) |
| throws CoreException { |
| if (option != null) { |
| if (option != Option.BUILD) { |
| throw new IllegalArgumentException(); |
| } |
| } |
| final IFolder folder = getFolder(folderName); |
| if (!folder.exists()) { |
| return; |
| } |
| int count = 0; |
| for (IResource member : folder.members()) { |
| member.delete(true, null); |
| ++count; |
| } |
| if (count != 0 && option == Option.BUILD) { |
| build(); |
| } |
| } |
| |
| protected IWorkspaceRoot getWorkspaceRoot() { |
| return ResourcesPlugin.getWorkspace().getRoot(); |
| } |
| |
| } |