/*******************************************************************************
* Copyright © 2005, 2013 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*
*******************************************************************************/
package org.eclipse.edt.ide.core.internal.builder;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.edt.compiler.binding.FileBinding;
import org.eclipse.edt.compiler.binding.IPackageBinding;
import org.eclipse.edt.compiler.binding.IPartBinding;
import org.eclipse.edt.compiler.binding.ITypeBinding;
import org.eclipse.edt.compiler.core.IEGLConstants;
import org.eclipse.edt.compiler.core.ast.File;
import org.eclipse.edt.compiler.core.ast.Node;
import org.eclipse.edt.compiler.core.ast.Part;
import org.eclipse.edt.compiler.internal.core.builder.CappedProblemRequestor;
import org.eclipse.edt.compiler.internal.core.builder.IBuildNotifier;
import org.eclipse.edt.compiler.internal.core.builder.IMarker;
import org.eclipse.edt.compiler.internal.core.builder.IProblemRequestor;
import org.eclipse.edt.compiler.internal.core.lookup.BindingCreator;
import org.eclipse.edt.compiler.internal.core.lookup.DefaultCompilerOptions;
import org.eclipse.edt.compiler.internal.core.lookup.EnvironmentScope;
import org.eclipse.edt.compiler.internal.core.lookup.FileScope;
import org.eclipse.edt.compiler.internal.core.lookup.Scope;
import org.eclipse.edt.compiler.internal.egl2mof.Egl2Mof;
import org.eclipse.edt.compiler.internal.util.PackageAndPartName;
import org.eclipse.edt.ide.core.EDTCoreIDEPlugin;
import org.eclipse.edt.ide.core.internal.compiler.Compiler;
import org.eclipse.edt.ide.core.internal.dependency.DependencyGraphManager;
import org.eclipse.edt.ide.core.internal.dependency.DependencyInfo;
import org.eclipse.edt.ide.core.internal.generation.IDEContext;
import org.eclipse.edt.ide.core.internal.lookup.FileInfoManager;
import org.eclipse.edt.ide.core.internal.lookup.IFileInfo;
import org.eclipse.edt.ide.core.internal.lookup.ProjectBuildPathEntryManager;
import org.eclipse.edt.ide.core.internal.lookup.ProjectEnvironment;
import org.eclipse.edt.ide.core.internal.lookup.ProjectEnvironmentManager;
import org.eclipse.edt.ide.core.internal.lookup.ProjectInfo;
import org.eclipse.edt.ide.core.internal.lookup.ProjectInfoManager;
import org.eclipse.edt.mof.EObject;
import org.eclipse.edt.mof.MofSerializable;
import org.eclipse.edt.mof.egl.utils.TypeUtils;
import org.eclipse.edt.mof.serialization.IEnvironment;
import org.eclipse.edt.mof.utils.NameUtile;
import org.eclipse.osgi.util.NLS;
/**
* @author svihovec
*
*/
public abstract class AbstractProcessingQueue extends org.eclipse.edt.compiler.internal.core.builder.AbstractProcessingQueue {
protected ProjectInfo projectInfo;
private static final int MAX_COMPILE_LOOP = 5;
private IProject project;
private ProjectEnvironment projectEnvironment;
private IProcessorRequestor requestor;
public AbstractProcessingQueue(IProject project, IBuildNotifier notifier) {
super(notifier, DefaultCompilerOptions.getInstance());
this.project = project;
this.projectInfo = ProjectInfoManager.getInstance().getProjectInfo(project);
this.projectEnvironment = ProjectEnvironmentManager.getInstance().getProjectEnvironment(project);
}
@Override
protected boolean hasExceededMaxLoop() {
return compileLoop >= MAX_COMPILE_LOOP;
}
@Override
protected IPartBinding level03Compile(PackageAndPartName ppName) {
String qualifiedName = getQualifiedName(ppName.getPackageName(), ppName.getPartName());
if(Builder.DEBUG){
System.out.println("\nProcessing: " + qualifiedName); //$NON-NLS-1$
}
notifier.subTask(NLS.bind(BuilderResources.buildCompiling, qualifiedName));
IFile declaringFile = projectInfo.getPartOrigin(ppName.getPackageName(), ppName.getPartName()).getEGLFile();
Node partAST = ASTManager.getInstance().getAST(declaringFile, ppName.getPartName());
IPartBinding binding = new BindingCreator(projectEnvironment, ppName, partAST).getPartBinding();
binding.setEnvironment(projectEnvironment);
DependencyInfo dependencyInfo = new DependencyInfo();
Scope scope = createScope(ppName.getPackageName(), declaringFile, binding, dependencyInfo);
CappedProblemRequestor cappedProblemRequestor = new CappedProblemRequestor();
cappedProblemRequestor.setRequestor(new MarkerProblemRequestor(declaringFile, ppName.getPartName()));
Compiler.getInstance().compilePart(partAST, binding, scope, dependencyInfo, cappedProblemRequestor, compilerOptions);
if(binding.getKind() == ITypeBinding.FILE_BINDING){
validatePackageDeclaration(ppName.getPackageName(), declaringFile, partAST, ((FileBinding)binding), cappedProblemRequestor);
}
if(binding.getKind() != ITypeBinding.FILE_BINDING){
processCompiledPart(ppName.getPackageName(), ppName.getPartName(), qualifiedName, (Part)partAST, binding, declaringFile, dependencyInfo, cappedProblemRequestor);
}else{
processCompiledFilePart(ppName.getPackageName(), ppName.getPartName(), declaringFile);
}
// record dependency info
DependencyGraphManager.getInstance().getDependencyGraph(project).putPart(ppName.getPackageName(), ppName.getPartName(), org.eclipse.edt.ide.core.internal.utils.Util.getFilePartName(declaringFile), dependencyInfo);
if(Builder.DEBUG){
numErrorsReported += cappedProblemRequestor.getNumberOfProblemsReported();
totalUnitsCompiled++;
System.out.println("Finished Processing: " + qualifiedName + "\n"); //$NON-NLS-1$ //$NON-NLS-2$
}
return binding;
}
private void processCompiledFilePart(String packageName, String caseInsensitiveInternedString, IFile declaringFile) {
IFileInfo fileInfo = FileInfoManager.getInstance().getFileInfo(project, declaringFile.getProjectRelativePath());
// For a file part, always add all other parts that are in the file
// Do not broadcast the file part change to other projects
for (Iterator iter = fileInfo.getPartNames().iterator(); iter.hasNext();) {
String nextName = (String) iter.next();
// don't add the file part again
if(!NameUtile.equals(nextName, caseInsensitiveInternedString)){
addPartFromCompiledFile(new PackageAndPartName(fileInfo.getCaseSensitivePackageName(), fileInfo.getCaseSensitivePartName(nextName), packageName));
}
}
}
private void processCompiledPart(String packageName, String caseInsensitiveInternedString, String qualifiedName, Part partAST, IPartBinding binding, IFile declaringFile, DependencyInfo dependencyInfo, CappedProblemRequestor cappedProblemRequestor) {
notifier.subTask(NLS.bind(BuilderResources.buildCreatingIR, qualifiedName));
File fileAST = ASTManager.getInstance().getFileAST(declaringFile);
MofSerializable previousPart;
try {
EObject eobj = ProjectEnvironmentManager.getInstance().getProjectEnvironment(project).findPart(packageName, caseInsensitiveInternedString);
if (eobj instanceof MofSerializable) {
previousPart = (MofSerializable)eobj;
}
else {
previousPart = null;
}
}
catch (Exception e) {
// if we couldn't load the part, just assume it's structurally different
previousPart = null;
}
try {
MofSerializable part = createIRFromBoundAST(partAST, declaringFile, fileAST.getImportDeclarations(), cappedProblemRequestor);
if(previousPart == null || !TypeUtils.areStructurallyEquivalent(previousPart, part)){
notifier.subTask(NLS.bind(BuilderResources.buildAddingDependentsOf, qualifiedName));
// If a part binding does not previously exist, do not add dependents of this part, because we have added dependents already before we processed the part.
if(previousPart != null){
addDependents(packageName, caseInsensitiveInternedString);
}
if(requestor != null){
requestor.recordStructuralChange(packageName, caseInsensitiveInternedString, binding.getKind());
}
}
notifier.subTask(NLS.bind(BuilderResources.buildSavingIR, qualifiedName));
if (canSave(caseInsensitiveInternedString)) {
ProjectEnvironmentManager.getInstance().getProjectEnvironment(project).getIREnvironment().save(part, true);
}
} catch (RuntimeException e) {
cappedProblemRequestor.acceptProblem(partAST.getName(), IProblemRequestor.COMPILATION_EXCEPTION, new String[]{partAST.getName().getCanonicalName()});
Compiler.getInstance().logIRCreationException(e);
}
}
private MofSerializable createIRFromBoundAST(Part partAST, IFile declaringFile, List imports, IProblemRequestor problemRequestor) {
IEnvironment env = ProjectEnvironmentManager.getInstance().getProjectEnvironment(project).getIREnvironment();
Egl2Mof generator = new Egl2Mof(env);
return (MofSerializable)generator.convert(partAST, new IDEContext(declaringFile, projectEnvironment.getCompiler()), problemRequestor);
}
protected void addPartFromCompiledFile(PackageAndPartName ppName){}
@Override
protected IPartBinding level02Compile(PackageAndPartName ppName) {
return ProjectBuildPathEntryManager.getInstance().getProjectBuildPathEntry(project).compileLevel2Binding(ppName);
}
@Override
protected IPartBinding level01Compile(PackageAndPartName ppName) {
return projectEnvironment.level01Compile(ppName);
}
@Override
protected IPartBinding getPartBindingFromCache(String packageName, String partName) {
return ProjectBuildPathEntryManager.getInstance().getProjectBuildPathEntry(project).getPartBindingFromCache(packageName, partName);
}
protected String getQualifiedName(String packageName, String partName){
if (packageName.length() == 0 || new Path(partName).segmentCount() > 1){
//partName is a file part or no packageName
return partName;
}
String retVal = new Path(packageName).addTrailingSeparator().toString() + partName;
return retVal.replace(IPath.SEPARATOR,'.');
}
private Scope createScope(String packageName, IFile declaringFile, IPartBinding binding, DependencyInfo dependencyInfo) {
Scope scope;
if(binding.getKind() == ITypeBinding.FILE_BINDING){
scope = new EnvironmentScope(projectEnvironment, dependencyInfo);
}else{
String fileName = org.eclipse.edt.ide.core.internal.utils.Util.getFilePartName(declaringFile);
IPartBinding fileBinding = projectEnvironment.getPartBinding(packageName, fileName);
scope = new FileScope(new EnvironmentScope(projectEnvironment, dependencyInfo), (FileBinding)fileBinding, dependencyInfo);
}
return scope;
}
private void validatePackageDeclaration(String packageName, IFile declaringFile, Node partAST, FileBinding binding, IProblemRequestor problemRequestor) {
try{
IPackageBinding declaringPackage = binding.getDeclaringPackage();
if(declaringPackage != null && !NameUtile.equals(declaringPackage.getPackageName(), packageName)){
if(packageName.length() == 0){
// package name specified in default package
problemRequestor.acceptProblem(((File)partAST).getPackageDeclaration(), IProblemRequestor.PACKAGE_NAME_DOESNT_MATCH_DIRECTORY_STRUCTURE, new String[0]);
}else if(((File)partAST).hasPackageDeclaration()){
// incorrect package declaration
problemRequestor.acceptProblem(((File)partAST).getPackageDeclaration(), IProblemRequestor.PACKAGE_NAME_DOESNT_MATCH_DIRECTORY_STRUCTURE, new String[0]);
}else{
// missing package declaration
IPath packagePath = declaringFile.getProjectRelativePath().removeFileExtension().removeLastSegments(1);
packagePath = packagePath.removeFirstSegments(packagePath.segmentCount() - org.eclipse.edt.ide.core.internal.utils.Util.qualifiedNameToStringArray(packageName).length);
problemRequestor.acceptProblem(0, 0, IMarker.SEVERITY_ERROR, IProblemRequestor.PACKAGE_NAME_NOT_PROVIDED, new String[]{packagePath.toString().replace(IPath.SEPARATOR, '.')});
}
}else{
// package declaration must match package name exactly (case sensitive)
if(((File)partAST).hasPackageDeclaration()){
String packageDeclName = ((File)partAST).getPackageDeclaration().getName().getCanonicalName();
// get package path, minus source folder
IPath packagePath = declaringFile.getProjectRelativePath().removeFileExtension().removeLastSegments(1);
packagePath = packagePath.removeFirstSegments(packagePath.segmentCount() - org.eclipse.edt.ide.core.internal.utils.Util.qualifiedNameToStringArray(packageName).length);
if(!packageDeclName.equals(packagePath.toString().replace(IPath.SEPARATOR, IEGLConstants.PACKAGE_SEPARATOR.charAt(0)))){
//package name does not match case of package on file system
problemRequestor.acceptProblem(((File)partAST).getPackageDeclaration(), IProblemRequestor.PACKAGE_NAME_DOESNT_MATCH_DIRECTORY_STRUCTURE, IMarker.SEVERITY_ERROR, new String[0]);
}
}
}
}catch(RuntimeException e){
problemRequestor.acceptProblem(0, 0, IMarker.SEVERITY_ERROR, IProblemRequestor.COMPILATION_EXCEPTION, new String[]{binding.getName()});
EDTCoreIDEPlugin.getPlugin().log("Part Validation Failure", e); //$NON-NLS-1$
}
}
protected abstract void addDependents(String packageName, String partName);
protected abstract void addDependents(String qualifiedName);
public void setProcessorRequestor(IProcessorRequestor requestor){
this.requestor = requestor;
}
public void removePart(String packageName, String partName) {
pendingUnits.remove(new ProcessingUnitKey(packageName, partName));
}
@Override
protected void doAddPart(String packageName, String caseInsensitivePartName) {
PackageAndPartName ppName = projectInfo.getPackageAndPartName(packageName, caseInsensitivePartName);
addPart(new PackageAndPartName(ppName.getCaseSensitivePackageName(), ppName.getCaseSensitivePartName(), packageName));
}
}