/*******************************************************************************
* Copyright © 2008, 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.lookup.workingcopy;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.edt.compiler.ICompiler;
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.IRPartBinding;
import org.eclipse.edt.compiler.binding.ITypeBinding;
import org.eclipse.edt.compiler.core.ast.Node;
import org.eclipse.edt.compiler.internal.core.builder.CircularBuildRequestException;
import org.eclipse.edt.compiler.internal.core.compiler.BindingCompletor;
import org.eclipse.edt.compiler.internal.core.dependency.NullDependencyRequestor;
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.FileASTScope;
import org.eclipse.edt.compiler.internal.core.lookup.FileScope;
import org.eclipse.edt.compiler.internal.core.lookup.IEnvironment;
import org.eclipse.edt.compiler.internal.core.lookup.Scope;
import org.eclipse.edt.compiler.internal.core.utils.PartBindingCache;
import org.eclipse.edt.compiler.internal.util.BindingUtil;
import org.eclipse.edt.compiler.internal.util.PackageAndPartName;
import org.eclipse.edt.ide.core.internal.binding.PartRestoreFailedException;
import org.eclipse.edt.ide.core.internal.builder.ASTManager;
import org.eclipse.edt.ide.core.internal.compiler.workingcopy.WorkingCopyASTManager;
import org.eclipse.edt.ide.core.internal.compiler.workingcopy.WorkingCopyProcessingQueue;
import org.eclipse.edt.ide.core.internal.lookup.ProjectBuildPathEntry;
import org.eclipse.edt.ide.core.internal.lookup.ProjectBuildPathManager;
import org.eclipse.edt.ide.core.internal.lookup.ProjectEnvironment;
import org.eclipse.edt.ide.core.internal.partinfo.IPartOrigin;
import org.eclipse.edt.ide.core.internal.utils.Util;
import org.eclipse.edt.ide.core.utils.ProjectSettingsUtility;
import org.eclipse.edt.mof.EObject;
import org.eclipse.edt.mof.egl.Part;
import org.eclipse.edt.mof.egl.PartNotFoundException;
import org.eclipse.edt.mof.serialization.CachingObjectStore;
import org.eclipse.edt.mof.serialization.DeserializationException;
import org.eclipse.edt.mof.serialization.ObjectStore;
import org.eclipse.edt.mof.utils.NameUtile;
/**
* Project Build Path entries are caches used when binding an AST tree. A new WorkingCopyProjectBuildPathEntry must be created each time a set of AST trees
* is bound for a given set of IWorkingCopy elements.
*
* @author svihovec
*
*/
public class WorkingCopyProjectBuildPathEntry implements IWorkingCopyBuildPathEntry {
private class RealizingEnvironment implements IEnvironment{
@Override
public IPartBinding getPartBinding(String packageName, String partName) {
return WorkingCopyProjectBuildPathEntry.this.getPartBinding(packageName, partName, true);
}
@Override
public IPartBinding getNewPartBinding(PackageAndPartName ppName, int kind) {
return WorkingCopyProjectBuildPathEntry.this.getNewPartBinding(ppName, kind);
}
@Override
public boolean hasPackage(String packageName) {
return WorkingCopyProjectBuildPathEntry.this.hasPackage(packageName);
}
@Override
public IPackageBinding getRootPackage() {
return declaringEnvironment.getRootPackage();
}
@Override
public ICompiler getCompiler() {
return WorkingCopyProjectBuildPathEntry.this.getCompiler();
}
}
private PartBindingCache bindingCache;
private WorkingCopyProjectEnvironment declaringEnvironment;
private WorkingCopyProjectInfo projectInfo;
private IEnvironment realizingEnvironment;
private WorkingCopyProcessingQueue processingQueue;
private ObjectStore[] stores;
public WorkingCopyProjectBuildPathEntry(WorkingCopyProjectInfo projectInfo){
this.projectInfo = projectInfo;
this.bindingCache = new PartBindingCache();
this.stores = ProjectBuildPathEntry.EMPTY_STORES;
this.realizingEnvironment = new RealizingEnvironment();
}
public boolean hasPackage(String packageName) {
return projectInfo.hasPackage(packageName);
}
public IPartBinding getPartBinding(String packageName, String partName) {
return getPartBinding(packageName, partName, false);
}
public IPartBinding getPartBinding(String packageName, String partName, boolean force) {
IPartBinding result = null;
if(processingQueue != null) {
result = processingQueue.requestCompilationFor(packageName, partName, force);
}
if(result == null){
// Conceptually should check whether it has that part or not, but for performance reason we will try to grab it from
// the cache first.
// The existance of a part in the cache implies that the part does physically exist
result = bindingCache.get(packageName, partName);
if(result != null) {
return result;
}
else {
if (WorkingCopyProjectBuildPathManager.getInstance().getProjectBuildPath(projectInfo.getProject()).isBinary()) {
//IF this is a binary project, and we are requesting a binding for the file, we need to compile it at level2.
//All other requests for part bindings from a binary project should return null
IPartOrigin origin = projectInfo.getPartOrigin(packageName, partName);
if (origin != null && origin.getEGLFile() != null) {
// We are in a binary project with source. If the request is for the file binding, do not try to read it from disk (it wont be there)
if (Util.getFilePartName(origin.getEGLFile())== partName) {
try{
return compileLevel2Binding(projectInfo.getPackageAndPartName(packageName, partName));
}catch(CircularBuildRequestException e){
// Remove this part from the cache, so that it is not used incorrectly in the future
removePartBindingInvalid(packageName, partName);
throw e;
}
}
}
return null;
}
//RMERUI
if(WorkingCopyProjectBuildPathManager.getInstance().getProjectBuildPath(projectInfo.getProject()).isReadOnly()){
// It is a project with no source, read the IRs
return readPartBinding(packageName, partName);
}else{
if (projectInfo.hasPart(packageName, partName) != ITypeBinding.NOT_FOUND_BINDING) {
// This project has source, compile from the source
IPartOrigin partOrigin = projectInfo.getPartOrigin(packageName, partName);
boolean shouldCompiled = true;
if (partOrigin == null || partOrigin.getEGLFile() == null) {
shouldCompiled = false;
}
else {
if(partOrigin.getEGLFile().isReadOnly()) {
int index = partName.lastIndexOf(".");
if(index > -1) {//if the part name represents FileName, will continue Compilation
String fileExtension = partName.substring(index+1);
if(!"egl".equalsIgnoreCase(fileExtension)) {
shouldCompiled = false;
}
}
else {
shouldCompiled = false;
}
} //shouldCompiled &&
}
if( shouldCompiled) {
try{
return compileLevel2Binding(projectInfo.getPackageAndPartName(packageName, partName));
}catch(CircularBuildRequestException e){
// Remove this part from the cache, so that it is not used incorrectly in the future
removePartBindingInvalid(packageName, partName);
throw e;
}
}
else {
return null;
}
}
else {return null;}
}
}
}
return result;
}
public IPartBinding getNewPartBinding(PackageAndPartName ppName, int kind) {
IPartBinding partBinding = bindingCache.get(ppName.getPackageName(), ppName.getPartName());
if(partBinding == null || partBinding.getKind() != kind) {
partBinding = BindingUtil.createPartBinding(kind, ppName);
bindingCache.put(ppName.getPackageName(), ppName.getPartName(), partBinding);
}
else {
partBinding.clear();
partBinding.setValid(false);
}
return partBinding;
}
public void setDeclaringEnvironment(WorkingCopyProjectEnvironment projectEnvironment) {
this.declaringEnvironment = projectEnvironment;
}
public IPartBinding getPartBindingFromCache(String packageName, String partName) {
return bindingCache.get(packageName, partName);
}
public IPartBinding getCachedPartBinding(String packageName, String partName) {
return getPartBindingFromCache(packageName, partName);
}
// TODO - When we add support to avoid copying nested records into the parent record's IR, we can use this
// method to get better performance out of the WCC - this method will load the IR if the source is older than the
// IR, instead of recompiling everything all of the time
// private IPartBinding doGetPartBinding(String[] packageName, String partName) {
// // First, see if there is a working copy of this part that we need to use
// // If there is no working copy of the part, attempt to find the IR file for this binding
// // If the IR file exists, check to see if the modification date on this file is older than the modification date on the source file
//
// // Check to see if a source part exists for this part
// int partType = projectInfo.hasPart(packageName, partName);
// if(partType != ITypeBinding.NOT_FOUND_BINDING) {
// // We found a source part
// String caseSensitiveInternedPartName = projectInfo.getCaseSensitivePartName(packageName, partName);
// if(projectInfo.isWorkingCopy(packageName, partName)){
// // The part is a working copy
// return compileLevel2Binding(packageName, caseSensitiveInternedPartName);
// }else{
// // The part is not a working copy
// if(partType == ITypeBinding.FILE_BINDING){
// // we can't load file bindings from IRs, so always recompile them
// return compileLevel2Binding(packageName, caseSensitiveInternedPartName);
// }else{
// File irFile = getIRFile(packageName, partName.toLowerCase());
// if(irFile.exists()){
// long irLastModified = irFile.lastModified();
// long sourceLastModified = projectInfo.getPartOrigin(packageName, NameUtile.getAsName(partName)).getEGLFile().getModificationStamp();
//
// if(sourceLastModified > irLastModified){
// return compileLevel2Binding(packageName, caseSensitiveInternedPartName);
// }else{
// IPartBinding partBinding = readPartBinding(irFile);
// if(partBinding.isValid()){
// partBinding.setEnvironment(declaringEnvironment);
// bindingCache.put(packageName, NameUtile.getAsName(partName), partBinding);
// //? WorkingCopyASTManager.getInstance().reportNestedFunctions(partAST,declaringFile);
// return partBinding;
// }
// }
// }
// }
// }
// }else{
// File irFile = getIRFile(packageName, partName);
// if(irFile.exists()){
// IPartBinding partBinding = readPartBinding(irFile);
// if(partBinding.isValid()){
// partBinding.setEnvironment(declaringEnvironment);
// bindingCache.put(packageName, NameUtile.getAsName(partName), partBinding);
// //? WorkingCopyASTManager.getInstance().reportNestedFunctions(partAST,declaringFile);
// return partBinding;
// }
// }
// }
// return null;
// }
public IPartBinding compileLevel2Binding(PackageAndPartName ppName) {
IFile declaringFile = projectInfo.getPartOrigin(ppName.getPackageName(), ppName.getPartName()).getEGLFile();
Node partAST = WorkingCopyASTManager.getInstance().getAST(declaringFile, ppName.getPartName());
IPartBinding partBinding = new BindingCreator(declaringEnvironment, ppName, partAST).getPartBinding();
Scope scope;
if(partBinding.getKind() == ITypeBinding.FILE_BINDING){
scope = new EnvironmentScope(declaringEnvironment, NullDependencyRequestor.getInstance());
}else{
String fileName = Util.getFilePartName(declaringFile);
IPartBinding fileBinding = getPartBinding(ppName.getPackageName(), fileName, true);
if(!fileBinding.isValid()){
scope = new FileASTScope(new EnvironmentScope(declaringEnvironment, NullDependencyRequestor.getInstance()), (FileBinding)fileBinding, ASTManager.getInstance().getFileAST(declaringFile));
}else{
scope = new FileScope(new EnvironmentScope(declaringEnvironment, NullDependencyRequestor.getInstance()), (FileBinding)fileBinding, NullDependencyRequestor.getInstance());
}
}
BindingCompletor.getInstance().completeBinding(partAST, partBinding, scope, DefaultCompilerOptions.getInstance());
partBinding.setEnvironment(declaringEnvironment);
if (partBinding instanceof IRPartBinding) {
BindingUtil.setEnvironment(((IRPartBinding)partBinding).getIrPart(), declaringEnvironment);
}
bindingCache.put(ppName.getPackageName(), ppName.getPartName(), partBinding);
WorkingCopyASTManager.getInstance().reportNestedFunctions(partAST,declaringFile);
return partBinding;
}
/**
* Called by a level_01 compile to create a binding for a part when the processing queue is too long.
* This should only be called on 'Source' projects, as a read only project would result in the part being loaded
* directly from an IR instead of being compiled.
*/
@Override
public int hasPart(String packageName, String partName) {
return projectInfo.hasPart(packageName, partName);
}
public IProject getProject() {
return projectInfo.getProject();
}
@Override
public IPartOrigin getPartOrigin(String packageName, String partName) {
return projectInfo.getPartOrigin(packageName,partName);
}
@Override
public IEnvironment getRealizingEnvironment() {
return realizingEnvironment;
}
public void setProcessingQueue(WorkingCopyProcessingQueue processingQueue) {
this.processingQueue = processingQueue;
}
public void clear() {
bindingCache = new PartBindingCache();
for (ObjectStore store : stores) {
if (store instanceof CachingObjectStore) {
((CachingObjectStore)store).clearCache();
}
}
}
public boolean isZipFile(){
return false;
}
public boolean isProject(){
return true;
}
public String getID(){
return getProject().getName();
}
private EObject readPart(String packageName, String name) throws DeserializationException {
String key;
if (packageName != null && packageName.length() > 0) {
key = packageName + "." + name;
}
else {
key = name;
}
for (int i = 0; i < stores.length; i++) {
EObject ir = stores[i].get(key);
if (ir != null) {
return ir;
}
}
return null;
}
private IPartBinding readPartBinding(String packageName, String partName) {
try {
EObject ir = readPart(packageName, partName);
if (ir != null) {
IPartBinding partBinding = BindingUtil.createPartBinding(ir);
if (partBinding != null) {
bindingCache.put(packageName, partName, partBinding);
return partBinding;
}
}
return null;
}
catch(Exception e) {
throw new PartRestoreFailedException(packageName, partName, e);
}
}
public FileBinding getFileBinding(String packageName, String fileName, org.eclipse.edt.compiler.core.ast.File fileAST) {
String caseInsensitiveInternedFileName = NameUtile.getAsName(fileName);
FileBinding fileBinding = getFileBindingFromCache(packageName, caseInsensitiveInternedFileName);
if (fileBinding != null) {
return fileBinding;
}
PackageAndPartName ppName = new PackageAndPartName(org.eclipse.edt.compiler.Util.createCaseSensitivePackageName(fileAST), caseInsensitiveInternedFileName);
fileBinding = (FileBinding)new BindingCreator(declaringEnvironment, ppName, fileAST).getPartBinding();
fileBinding.setEnvironment(declaringEnvironment);
Scope scope = new EnvironmentScope(declaringEnvironment, NullDependencyRequestor.getInstance());
BindingCompletor.getInstance().completeBinding(fileAST, fileBinding, scope, DefaultCompilerOptions.getInstance());
bindingCache.put(packageName, caseInsensitiveInternedFileName, fileBinding);
return fileBinding;
}
public FileBinding getFileBindingFromCache(String packageName, String partName){
return (FileBinding)bindingCache.get(packageName, partName);
}
private ICompiler getCompiler() {
return ProjectSettingsUtility.getCompiler(getProject());
}
protected void setObjectStores(ObjectStore[] stores) {
if (stores == null) {
this.stores = ProjectBuildPathEntry.EMPTY_STORES;
}
else {
this.stores = stores;
}
}
@Override
public ObjectStore[] getObjectStores() {
return stores;
}
@Override
public org.eclipse.edt.mof.egl.Part findPart(String packageName, String name) throws PartNotFoundException {
if(ProjectBuildPathManager.getInstance().getProjectBuildPath(projectInfo.getProject()).isReadOnly()
|| projectInfo.hasPart(packageName, name) != ITypeBinding.NOT_FOUND_BINDING){
try {
EObject ir = readPart(packageName, name);
if (ir instanceof Part) {
return (Part)ir;
}
}
catch (DeserializationException e) {
throw new PartNotFoundException(e);
}
}
return null;
}
/**
* Remove this part binding from the cache since it has been removed from the workspace
*/
public void removePartBindingInvalid(String packageName, String partName) {
bindingCache.remove(packageName, partName);
}
protected WorkingCopyProjectEnvironment getDeclaringEnvironment() {
return this.declaringEnvironment;
}
}