/******************************************************************************* * Copyright (c) 2012 NumberFour AG * * 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: * 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(); } }