blob: 0988affd3c6064fa37937a0efcbb7ce0a2fce814 [file] [log] [blame]
/********************************************************************
* Copyright (c) 2007 Contributors. 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://eclipse.org/legal/epl-v10.html
*
* Contributors: IBM Corporation - initial API and implementation
* Helen Hawkins - initial version (bug 148190)
*******************************************************************/
package org.eclipse.ajdt.internal.core.ajde;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.aspectj.ajde.core.ICompilerConfiguration;
import org.aspectj.ajde.core.IOutputLocationManager;
import org.aspectj.ajdt.internal.core.builder.CompilerConfigurationChangeFlags;
import org.eclipse.ajdt.core.AJLog;
import org.eclipse.ajdt.core.AopXmlPreferences;
import org.eclipse.ajdt.core.AspectJCorePreferences;
import org.eclipse.ajdt.core.AspectJPlugin;
import org.eclipse.ajdt.core.BuildConfig;
import org.eclipse.ajdt.core.CoreUtils;
import org.eclipse.ajdt.core.text.CoreMessages;
import org.eclipse.ajdt.internal.core.builder.BuildClasspathResolver;
import org.eclipse.core.internal.content.ContentTypeManager;
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.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.apt.core.internal.JarFactoryContainer;
import org.eclipse.jdt.apt.core.internal.util.FactoryContainer;
import org.eclipse.jdt.apt.core.internal.util.FactoryPath;
import org.eclipse.jdt.apt.core.internal.util.FactoryPathUtil;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.core.ClasspathEntry;
import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.jdt.internal.core.util.Util;
import org.eclipse.osgi.util.NLS;
/**
* ICompilerConfiguration implementation which returns information for all
* methods except getNonStandardOptions(). This implementation is used
* if ajdt.core plugin is not present in the platform
*/
public class CoreCompilerConfiguration implements ICompilerConfiguration {
private String cachedClasspath = null;
protected IProject project;
protected CoreOutputLocationManager locationManager;
protected FileURICache fileCache;
// fully qualified list of file names that have been touched since
// last build
// set to null originally since we don't know anything
// about build state when first created. Assume everything
// has changed.
private List<File> modifiedFiles = null;
// set of flags describing what has changed since last
// build
// initially set to EVERYTHING since we don't know
// build state when first created.
private int configurationChanges;
// list of classpath entries that have been rebuilt since last build
private List<String> classpathElementsWithModifiedContents = null;
public CoreCompilerConfiguration(IProject project) {
this.project = project;
AJLog.log(AJLog.BUILDER, "Compiler configuration for project " + project.getName() + " doesn't know previous state, so assuming EVERYTHING has changed.");
configurationChanges = EVERYTHING;
fileCache = new FileURICache(project);
}
public void buildStarting() {
((CoreOutputLocationManager) getOutputLocationManager()).buildStarting();
}
public void buildComplete() {
((CoreOutputLocationManager) getOutputLocationManager()).buildComplete();
}
@SuppressWarnings("unchecked")
public Map<String, String> getJavaOptionsMap() {
Map<String, String> optionsMap = null;
JavaProject javaProject;
try {
javaProject = (JavaProject) project.getNature(JavaCore.NATURE_ID);
optionsMap = javaProject.getOptions(true);
} catch (CoreException e) {
}
if (optionsMap == null) {
return JavaCore.getOptions();
} else {
return optionsMap;
}
}
public String getNonStandardOptions() {
// ajdt.ui supplies impl
return ""; //$NON-NLS-1$
}
public Set<File> getAspectPath() {
String[] v = AspectJCorePreferences.getResolvedProjectAspectPath(project);
// need to expand any variables on the path
String aspectpath = expandVariables(v[0], v[2]);
// Ensure that every entry in the list is a fully qualified one.
aspectpath = fullyQualifyPathEntries(aspectpath);
if (aspectpath.length() == 0)
return null;
return mapStringToSet(aspectpath, false);
}
public String getClasspath() {
if (cachedClasspath != null)
return cachedClasspath;
IJavaProject jp = JavaCore.create(project);
// bug 73035: use this build classpath resolver which is a direct
// copy from JDT, so the classpath environment is much closer between
// AspectJ and Java projects.
cachedClasspath = new BuildClasspathResolver().getClasspath(AspectJPlugin.getWorkspace().getRoot(), jp);
return cachedClasspath;
}
public Set<File> getInpath() {
String[] v = AspectJCorePreferences.getResolvedProjectInpath(project);
// need to expand any variables on the path
String inpath = expandVariables(v[0], v[2]);
// Ensure that every entry in the list is a fully qualified one.
inpath = fullyQualifyPathEntries(inpath);
if (inpath.length() == 0)
return null;
return mapStringToSet(inpath, false);
}
public String getOutJar() {
String outputJar = AspectJCorePreferences.getProjectOutJar(project);
// If outputJar does not start with a slash, we might need to prepend
// the project work directory.
if (outputJar.trim().length() > 0 && !(outputJar.startsWith("\\") || outputJar.startsWith("/"))) { //$NON-NLS-1$ //$NON-NLS-2$
String trimmedName = outputJar.trim();
boolean prependProject = true;
// It might still be a fully qualified path if the 2nd char is a ':'
// (i.e. its
// a windows absolute path with a drive letter in it !)
if (trimmedName.length() > 1) {
if (trimmedName.charAt(1) == ':')
prependProject = false;
}
if (prependProject) {
// Its a relative path, it should be relative to the project.
String projectBaseDirectory = project.getLocation().toOSString();
outputJar = projectBaseDirectory + File.separator + outputJar.trim();
}
}
return outputJar;
}
public IOutputLocationManager getOutputLocationManager() {
if (locationManager == null) {
locationManager = new CoreOutputLocationManager(project, fileCache);
}
return locationManager;
}
/**
* bug 270335 need to recreate locationManager if any of the
* following configuration changes have occurred:
* ASPECTPATH_CHANGED | CLASSPATH_CHANGED | INPATH_CHANGED |
* OUTJAR_CHANGED | OUTPUTDESTINATIONS_CHANGED | INJARS_CHANGED
*
* @return true if {@link #locationManager} field has been reset to
* null. false otherwise
*/
public boolean flushOutputLocationManagerIfNecessary(int buildKind) {
if (buildKind == IncrementalProjectBuilder.FULL_BUILD ||
buildKind == IncrementalProjectBuilder.CLEAN_BUILD) {
locationManager = null;
} else if ((configurationChanges &
(ASPECTPATH_CHANGED | CLASSPATH_CHANGED |
INPATH_CHANGED | OUTJAR_CHANGED |
OUTPUTDESTINATIONS_CHANGED | INJARS_CHANGED)) != 0) {
locationManager = null;
} else {
// we don't know if bin folders of depending projects
// have changed, so just assume that they have
if (locationManager != null) {
locationManager.zapBinFolderToProjectMap();
}
}
return locationManager == null;
}
public List<String> getProjectSourceFiles() {
Set<IFile> files = BuildConfig.getIncludedSourceFiles(project);
List<String> iofiles = new ArrayList<String>(files.size());
for (IFile f : files) {
iofiles.add(f.getLocation().toOSString());
}
return iofiles;
}
/**
* AJDT handles resource copying for all builds, so always return null here.
*/
public Map<String, File> getSourcePathResources() {
return null;
}
public void flushClasspathCache() {
cachedClasspath = null;
}
public String expandVariables(String path, String eKinds) {
StringBuffer resultBuffer = new StringBuffer();
StringTokenizer strTok = new StringTokenizer(path, File.pathSeparator);
StringTokenizer strTok2 = new StringTokenizer(eKinds, File.pathSeparator);
while (strTok.hasMoreTokens()) {
String current = strTok.nextToken();
int entryKind = Integer.parseInt(strTok2.nextToken());
if (entryKind == IClasspathEntry.CPE_VARIABLE) {
int slashPos = current.indexOf(AspectJPlugin.NON_OS_SPECIFIC_SEPARATOR, 0);
if (slashPos != -1) {
String exp = JavaCore.getClasspathVariable(current.substring(0, slashPos)).toOSString();
resultBuffer.append(exp);
resultBuffer.append(current.substring(slashPos));
} else {
String exp = JavaCore.getClasspathVariable(current).toOSString();
resultBuffer.append(exp);
}
} else {
resultBuffer.append(current);
}
resultBuffer.append(File.pathSeparator);
}
return resultBuffer.toString();
}
/**
* @param inputPath
* @return
*/
public String fullyQualifyPathEntries(String inputPath) {
StringBuffer resultBuffer = new StringBuffer();
StringTokenizer strTok = new StringTokenizer(inputPath, File.pathSeparator);
while (strTok.hasMoreTokens()) {
String current = strTok.nextToken();
File f = new File(current);
if (f.exists() && f.isAbsolute()) {
// entry not relative to workspace (it's fully qualifed)
resultBuffer.append(current);
} else {
// Try to resolve path relative to the workspace. Need to
// replace part of the path string with a fully qualified
// equivalent.
String projectName = null;
int slashPos = current.indexOf(AspectJPlugin.NON_OS_SPECIFIC_SEPARATOR, 1);
if (slashPos != -1) {
projectName = current.substring(1, slashPos);
} else {
projectName = current.substring(1);
}
IProject proj = AspectJPlugin.getWorkspace().getRoot().getProject(projectName);
if (proj != null && proj.getLocation() != null) {
String projectPath = proj.getLocation().toString();
if (slashPos != -1) {
String rest = current.substring(slashPos + 1);
IResource res = proj.findMember(rest);
if (res != null) {
resultBuffer.append(res.getRawLocation().toOSString());
} else {
resultBuffer.append(projectPath + AspectJPlugin.NON_OS_SPECIFIC_SEPARATOR + rest);
}
} else {
resultBuffer.append(projectPath);
}
}// end if named project found
else {
AJLog.log(AJLog.BUILDER, "AspectJ path entry " + current //$NON-NLS-1$
+ " does not exist."); //$NON-NLS-1$
resultBuffer.append(current);
}// end else entry not found in workspace
}// end if entry is relative to workspace
resultBuffer.append(File.pathSeparator);
}// end while more tokens to process
String result = resultBuffer.toString();
if (result.endsWith(File.pathSeparator)) {
result = result.substring(0, result.length() - 1);
}
return result;
}
/**
* Utility method for converting a semicolon separated list of files stored in a string into a Set of {@link File} objects.
*
*/
private Set<File> mapStringToSet(String input, boolean validateFiles) {
if (input.length() == 0)
return null;
String inputCopy = input;
StringBuffer invalidEntries = new StringBuffer();
// For relative paths (they don't start with a File.separator
// or a drive letter on windows) - we prepend the projectBaseDirectory
String projectBaseDirectory = project.getLocation().toOSString();
Set<File> fileSet = new HashSet<File>();
while (inputCopy.indexOf(File.pathSeparator) != -1) {
int idx = inputCopy.indexOf(File.pathSeparator);
String path = inputCopy.substring(0, idx);
File f = new File(path);
if (!f.isAbsolute())
f = new File(projectBaseDirectory + File.separator + path);
if (validateFiles && !f.exists()) {
invalidEntries.append(f + "\n"); //$NON-NLS-1$
} else {
fileSet.add(f);
}
inputCopy = inputCopy.substring(idx + 1);
}
// Process the final element
if (inputCopy.length() != 0) {
File f = new File(inputCopy);
if (!f.isAbsolute())
f = new File(projectBaseDirectory + File.separator + inputCopy);
if (validateFiles && !f.exists()) {
invalidEntries.append(f + "\n"); //$NON-NLS-1$
} else {
fileSet.add(f);
}
}
if (validateFiles && invalidEntries.length() != 0) {
AJLog.log(AJLog.COMPILER, NLS.bind(CoreMessages.missingJarsWarning, invalidEntries.toString()));
}
return fileSet;
}
private void getProjectRelativePaths(IResource[] resource_list, List<String> allProjectFiles, CoreUtils.FilenameFilter filter,
int trimSegments, IClasspathEntry sourceEntry) {
try {
for (int i = 0; i < resource_list.length; i++) {
IResource ir = resource_list[i];
// bug 161739: skip excluded resources
char[][] inclusionPatterns = ((ClasspathEntry) sourceEntry).fullInclusionPatternChars();
char[][] exclusionPatterns = ((ClasspathEntry) sourceEntry).fullExclusionPatternChars();
if (!Util.isExcluded(ir, inclusionPatterns, exclusionPatterns)) {
if (ir instanceof IContainer) {
getProjectRelativePaths(((IContainer) ir).members(), allProjectFiles, filter, trimSegments, sourceEntry);
} else if (filter.accept(ir.getName())) {
String[] segments = ir.getProjectRelativePath().segments();
String path = ""; //$NON-NLS-1$
for (int j = trimSegments; j < segments.length; j++) {
path += segments[j];
if (j < segments.length - 1)
path += '/'; // matches Eclipse's separator
}
allProjectFiles.add(path);
}
}
}
} catch (Exception e) {
}
}
private ArrayList<IResource> getLinkedChildFolders(IResource resource) {
ArrayList<IResource> resultList = new ArrayList<IResource>();
if (resource instanceof IContainer) {
try {
IResource[] children = ((IContainer) resource).members();
for (int i = 0; i < children.length; i++) {
if ((children[i] instanceof IFolder) && children[i].isLinked()) {
resultList.add(children[i]);
}
}
} catch (CoreException e) {
}
}
return resultList;
}
private String getResourceFullPath(IResource srcContainer, String relPath, ArrayList<IResource> linkedFolders) {
String result = null;
if (relPath.lastIndexOf('/') != -1) {
// Check to see if the relPath under scrutiny is
// under a linked folder in this project.
Iterator<IResource> it = linkedFolders.iterator();
while (it.hasNext()) {
IFolder folder = (IFolder) it.next();
String linkedFolderName = folder.getName();
if (relPath.indexOf(linkedFolderName + "/") == 0) { //$NON-NLS-1$
// Do the replacement ensuring that the result uses
// operating system separator characters.
result = folder.getLocation().toString() + relPath.substring(linkedFolderName.length());
result = result.replace('/', File.separatorChar);
break;
}
}
}
if (result == null) {
result = srcContainer.getLocation().toOSString() + File.separator + relPath;
}
return result;
}
public void addModifiedFile(File changedFile) {
AJLog.log(AJLog.BUILDER, "File: " + changedFile + " has changed.");
if (modifiedFiles != null) {
modifiedFiles.add(changedFile);
} else {
AJLog.log(AJLog.BUILDER, " but, we don't have any state yet, so not recording the change.");
}
}
/**
* Flag this compiler configuration as having had a change.
* This is reset after a call to {@link #configurationRead()}
* @param changeFlag change flag from
* {@link CompilerConfigurationChangeFlags}
*/
public void configurationChanged(int changeFlag) {
configurationChanges |= changeFlag;
logConfigurationChange(changeFlag);
}
private void logConfigurationChange(int changeFlag) {
List<String> changeKind = new ArrayList<String>();
if ((changeFlag & PROJECTSOURCEFILES_CHANGED) != NO_CHANGES) {
changeKind.add("PROJECTSOURCEFILES_CHANGED");
}
if ((changeFlag & JAVAOPTIONS_CHANGED) != NO_CHANGES) {
changeKind.add("JAVAOPTIONS_CHANGED");
}
if ((changeFlag & ASPECTPATH_CHANGED) != NO_CHANGES) {
changeKind.add("ASPECTPATH_CHANGED");
}
if ((changeFlag & CLASSPATH_CHANGED) != NO_CHANGES) {
changeKind.add("CLASSPATH_CHANGED");
}
if ((changeFlag & INPATH_CHANGED) != NO_CHANGES) {
changeKind.add("INPATH_CHANGED");
}
if ((changeFlag & NONSTANDARDOPTIONS_CHANGED) != NO_CHANGES) {
changeKind.add("NONSTANDARDOPTIONS_CHANGED");
}
if ((changeFlag & OUTJAR_CHANGED) != NO_CHANGES) {
changeKind.add("OUTJAR_CHANGED");
}
if ((changeFlag & PROJECTSOURCERESOURCES_CHANGED) != NO_CHANGES) {
changeKind.add("PROJECTSOURCERESOURCES_CHANGED");
}
if ((changeFlag & OUTPUTDESTINATIONS_CHANGED) != NO_CHANGES) {
changeKind.add("OUTPUTDESTINATIONS_CHANGED");
}
// deprecated
if ((changeFlag & INJARS_CHANGED) != NO_CHANGES) {
changeKind.add("INJARS_CHANGED");
}
AJLog.log(AJLog.BUILDER, "CoreCompilerConfiguration for project " + project.getName() + " registered a configuration change: " + changeKind);
}
/**
* converts the current configuration change list to a
* human readable string
* @return human readable string denoting all configuration
* changes since last read.
*/
private String toConfigurationString() {
List<String> changeKind = new ArrayList<String>();
if ((configurationChanges & PROJECTSOURCEFILES_CHANGED) != NO_CHANGES) {
changeKind.add("PROJECTSOURCEFILES_CHANGED");
}
if ((configurationChanges & JAVAOPTIONS_CHANGED) != NO_CHANGES) {
changeKind.add("JAVAOPTIONS_CHANGED");
}
if ((configurationChanges & ASPECTPATH_CHANGED) != NO_CHANGES) {
changeKind.add("ASPECTPATH_CHANGED");
}
if ((configurationChanges & CLASSPATH_CHANGED) != NO_CHANGES) {
changeKind.add("CLASSPATH_CHANGED");
}
if ((configurationChanges & INPATH_CHANGED) != NO_CHANGES) {
changeKind.add("INPATH_CHANGED");
}
if ((configurationChanges & NONSTANDARDOPTIONS_CHANGED) != NO_CHANGES) {
changeKind.add("NONSTANDARDOPTIONS_CHANGED");
}
if ((configurationChanges & OUTJAR_CHANGED) != NO_CHANGES) {
changeKind.add("OUTJAR_CHANGED");
}
if ((configurationChanges & PROJECTSOURCERESOURCES_CHANGED) != NO_CHANGES) {
changeKind.add("PROJECTSOURCERESOURCES_CHANGED");
}
if ((configurationChanges & OUTPUTDESTINATIONS_CHANGED) != NO_CHANGES) {
changeKind.add("OUTPUTDESTINATIONS_CHANGED");
}
// deprecated
if ((configurationChanges & INJARS_CHANGED) != NO_CHANGES) {
changeKind.add("INJARS_CHANGED");
}
return changeKind.toString();
}
/**
* Callback method from AspectJ to tell us that it has processed the configuration information and is going to proceed with a
* build.
*/
public void configurationRead() {
// we now know nothing has changed
AJLog.log(AJLog.BUILDER, "Compiler configuration for project " + project.getName() + " has been read by compiler. Resetting.");
AJLog.log(AJLog.BUILDER, " Configuration was " + toConfigurationString());
// we are still not keeping track of some changes:
// JAVAOPTIONS_CHANGED | NONSTANDARDOPTIONS_CHANGED | OUTJAR_CHANGED |
// OUTPUTDESTINATIONS_CHANGED | INJARS_CHANGED
configurationChanges = NO_CHANGES;
resetModifiedList();
}
/**
* Need to tell AspectJ what has changed in the configuration since the last build was done - the lazy answer (which causes it
* to behave as it always used to) is EVERYTHING.
*
* @see CompilerConfigurationChangeFlags
* @see AspectJCorePreferences#isIncrementalCompilationOptimizationsEnabled
*/
public int getConfigurationChanges() {
// if the optimization flag is turned off, then return EVERYTHING
if (!AspectJCorePreferences.isIncrementalCompilationOptimizationsEnabled()) {
AJLog.log(AJLog.BUILDER, "Optimizations turned off, so assuming all parts of configuration have changed");
return EVERYTHING;
} else {
AJLog.log(AJLog.BUILDER, "Sending the following configuration changes to the compiler: " + toConfigurationString());
return AspectJCorePreferences.isIncrementalCompilationOptimizationsEnabled() ? configurationChanges : EVERYTHING;
}
}
/**
* If we know, tell AspectJ a List<File> that have changed since the last build. We should be able to work this out from
* analysing delta changes. Returning null means we have no idea and will cause AspectJ to do the analysis to work it out.
*/
public List<File> getProjectSourceFilesChanged() {
if (!AspectJCorePreferences.isIncrementalCompilationOptimizationsEnabled()) {
AJLog.log(AJLog.BUILDER, "Optimizations turned off, so assuming all source files have changed");
return null;
} else if (modifiedFiles == null) {
// null means we dont know
AJLog.log(AJLog.BUILDER, "We don't know what has changed since last build, so assuming all source files have changed");
return null;
} else {
AJLog.log(AJLog.BUILDER, modifiedFiles.size() + " source file changes since last build");
return modifiedFiles;
}
}
public void resetModifiedList() {
AJLog.log(AJLog.BUILDER, "Resetting list of modified source files. Was " +
(modifiedFiles == null ? "null" : modifiedFiles.toString()));
modifiedFiles = new ArrayList<File>();
}
public void resetClasspathElementsWithModifiedContents() {
classpathElementsWithModifiedContents = null;
}
public void setClasspathElementsWithModifiedContents(List<String> modifiedContents) {
AJLog.log(AJLog.BUILDER, "Setting list of classpath elements with modified contents:");
AJLog.log(AJLog.BUILDER, " " + (modifiedContents == null ? "NULL" : modifiedContents.toString()));
classpathElementsWithModifiedContents = modifiedContents;
}
// must go through the classpath and look at projects we depend on that have been built before our
// most recent last build
public List<String> getClasspathElementsWithModifiedContents() {
return classpathElementsWithModifiedContents;
}
/**
* helper method that grabs the compiler configuration for a particular project
* creates one if it does not exist
* @param project
* @return the project's compiler configuration
*/
public static CoreCompilerConfiguration getCompilerConfigurationForProject(IProject project) {
return (CoreCompilerConfiguration) AspectJPlugin.getDefault().getCompilerFactory().getCompilerForProject(project).getCompilerConfiguration();
}
public File[] getCompiledSourceFiles() {
CoreOutputLocationManager coreOutputLocationManager = (CoreOutputLocationManager) getOutputLocationManager();
File[] compiledSourceFiles =
coreOutputLocationManager.getCompiledSourceFiles();
return compiledSourceFiles;
}
public List<String> getProjectXmlConfigFiles() {
return new AopXmlPreferences(project).getAopXmlFilesAsListOfStrings();
}
public String getProjectEncoding() {
try {
String charset = project.getDefaultCharset(false);
if (charset == null) {
charset = ContentTypeManager.getInstance().findContentTypeFor("f.java").getDefaultCharset();
}
if (charset == null) {
charset = ResourcesPlugin.getEncoding();
}
return charset;
} catch (CoreException e) {
return ResourcesPlugin.getEncoding();
}
}
public FileURICache getFileCache() {
return fileCache;
}
public String getProcessor() {
return null;
}
public String getProcessorPath() {
// Grab the factory entries from the Java Compiler annotation project properties page
IJavaProject jp = JavaCore.create(project);
FactoryPath fp = FactoryPathUtil.getFactoryPath(jp);
Map<FactoryContainer,FactoryPath.Attributes> containers = fp.getEnabledContainers();
ArrayList<File> fileList = new ArrayList<File>( containers.size() );
for (Map.Entry<FactoryContainer, FactoryPath.Attributes> entry : containers.entrySet()) {
FactoryPath.Attributes attr = entry.getValue();
FactoryContainer fc = entry.getKey();
if (!attr.runInBatchMode() && fc instanceof JarFactoryContainer) {
JarFactoryContainer jfc = (JarFactoryContainer)fc;
fileList.add( jfc.getJarFile() );
}
}
StringBuilder fcp = new StringBuilder();
for (File f: fileList) {
fcp.append(f.getAbsolutePath());
}
return fcp.toString();
}
}