/*******************************************************************************
* Copyright (c) 2008, 2014 Spring IDE Developers
* 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:
* Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.beans.core.model.locate;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.JavaModelException;
import org.springframework.ide.eclipse.beans.core.BeansCorePlugin;
import org.springframework.ide.eclipse.beans.core.model.IBeansConfig;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
/**
* Abstract {@link IBeansConfigLocator} implementation that uses ant file patterns to located
* {@link IBeansConfig} candidates in the given {@link IProject}.
* <p>
* File patterns need to provided via the {@link #getAllowedFilePatterns()} method. To fine control
* the matching process this implementation offers the {@link #canLocateInProject(IProject)} and
* {@link #filterMatchingFiles(Set)}.
* <p>
* Root directories to recursively search need to be provided by sub-classes by implementing the
* {@link #getRootDirectories(IProject)} method.
*
* @author Christian Dupuis
* @author Martin Lippert
* @since 2.0.5
*/
public abstract class AbstractPathMatchingBeansConfigLocator extends AbstractBeansConfigLocator {
/** Internal path matcher that understands ant patterns */
private PathMatcher pathMatcher = new AntPathMatcher();
/**
* Locates potential {@link IFile}s. Uses ant file name patterns to match all resources of a
* project recursively.
* @see #canLocateInProject(IProject)
* @see #filterMatchingFiles(Set)
* @see #getAllowedFilePatterns()
*/
@Override
public final Set<IFile> locateBeansConfigs(IProject project, IProgressMonitor progressMonitor) {
if (progressMonitor == null) {
progressMonitor = new NullProgressMonitor();
}
progressMonitor.beginTask(String.format(
"Scanning for Spring configuration files in project '%s'", project.getName()), 3);
try {
Set<IFile> files = new LinkedHashSet<IFile>();
if (canLocateInProject(project)) {
progressMonitor.worked(1);
try {
for (IPath rootDir : getRootDirectories(project)) {
if (progressMonitor.isCanceled()) {
return Collections.emptySet();
}
progressMonitor.subTask(String.format(
"Scanning for Spring configuration files in folder '%s'",
rootDir.toString()));
IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(
rootDir);
if (resource instanceof IFolder) {
locateConfigsInFolder(files, project, (IFolder) resource, rootDir);
}
else if (resource instanceof IContainer) {
for (IResource res : ((IContainer) resource).members()) {
if (res instanceof IFolder) {
locateConfigsInFolder(files, project, (IFolder) res, rootDir);
}
else if (res instanceof IFile) {
locateConfigInFile(files, project, rootDir, (IFile) res);
}
}
}
}
}
catch (JavaModelException e) {
BeansCorePlugin.log(e);
}
catch (CoreException e) {
BeansCorePlugin.log(e);
}
}
if (progressMonitor.isCanceled()) {
return Collections.emptySet();
}
progressMonitor.worked(1);
return filterMatchingFiles(files);
}
finally {
progressMonitor.worked(1);
progressMonitor.done();
}
}
/**
* Prepends '/' to the filePath and pattern if not already in place. Calls {@link #pathMatcher}
* with both.
*/
private boolean matches(String filePath, String pattern) {
if (!filePath.startsWith("/")) {
filePath = new StringBuilder().append("/").append(filePath).toString();
}
if (!pattern.startsWith("/")) {
pattern = new StringBuilder().append("/").append(pattern).toString();
}
return pathMatcher.match(pattern, filePath);
}
/**
* Pre-check to make sure that this implementation supports a the given project and its type.
* <p>
* Sub-classes may wish to override this method to do own pre-checks.
* <p>
* This default implementation just checks if the given <code>project</code> is a java
* project.
* @param project the project to check if this implementation can search of {@link IBeansConfig}
* files
* @return true if the given project should be scanned for files
*/
protected boolean canLocateInProject(IProject project) {
return true;
}
/**
* Sub-classes may override this method to provide additional locating logic.
* <p>
* This default implementation is just empty.
* @param files the already located files
* @param javaProject the current java project
* @param file the file we are currently looking at
*/
protected void doLocateConfig(Set<IFile> files, IProject project, IFile file) {
// no-op
}
/**
* Filter path matching files. This method provides a post-processing hook for filtering found
* files. Sub-classes might want to override this method to filter out any un-wanted files.
* <p>
* This default implementation just passes through the given set of files.
* @param files the files to filter
* @return the {@link Set} of filtered files
*/
protected Set<IFile> filterMatchingFiles(Set<IFile> files) {
return files;
}
/**
* Method to provide file patterns that should be used for searching.
*/
protected abstract Set<String> getAllowedFilePatterns();
/**
* Locates matching files in the given <code>folder</code>. Walks down the file tree until no
* further sub-folder is found.
*/
protected void locateConfigsInFolder(Set<IFile> files, IProject project, IFolder folder,
IPath rootDir) throws CoreException {
if (folder != null && folder.exists() && !folder.isDerived()) {
for (IResource resource : folder.members()) {
if (resource instanceof IFile) {
locateConfigInFile(files, project, rootDir, (IFile) resource);
}
else if (resource instanceof IFolder) {
locateConfigsInFolder(files, project, (IFolder) resource, rootDir);
}
}
}
}
/**
* Locates configs in the given <code>resource</code>.
*/
protected void locateConfigInFile(Set<IFile> files, IProject project, IPath rootDir,
IFile resource) {
if (!resource.isDerived()) {
String filePath = removeRootDir(resource.getFullPath(), rootDir);
for (String pattern : getAllowedFilePatterns()) {
String fileExtension = resource.getFileExtension();
if (fileExtension != null && getAllowedFileExtensions().contains(fileExtension)
&& matches(filePath, pattern)) {
files.add(resource);
}
doLocateConfig(files, project, resource);
}
}
}
/**
* Remove the given root directory path from the file path and returns the String.
*/
protected String removeRootDir(IPath filePath, IPath rootDir) {
int segCount = filePath.matchingFirstSegments(rootDir);
return filePath.removeFirstSegments(segCount).toString();
}
}