| package org.eclipse.photran.internal.core.vpg; |
| |
| import java.util.ArrayList; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.TreeSet; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.photran.core.IFortranAST; |
| import org.eclipse.photran.internal.core.FProjectNature; |
| import org.eclipse.photran.internal.core.FortranCorePlugin; |
| import org.eclipse.photran.internal.core.analysis.binding.Definition; |
| import org.eclipse.photran.internal.core.analysis.binding.Definition.Visibility; |
| import org.eclipse.photran.internal.core.analysis.binding.ScopingNode; |
| import org.eclipse.photran.internal.core.analysis.types.Type; |
| import org.eclipse.photran.internal.core.lexer.Token; |
| import org.eclipse.photran.internal.core.parser.ASTErrorConstructNode; |
| import org.eclipse.photran.internal.core.parser.ASTErrorProgramUnitNode; |
| import org.eclipse.photran.internal.core.parser.ASTExecutableProgramNode; |
| import org.eclipse.photran.internal.core.parser.ASTExternalNameListNode; |
| import org.eclipse.photran.internal.core.parser.ASTExternalStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTFunctionStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTFunctionSubprogramNode; |
| import org.eclipse.photran.internal.core.parser.ASTModuleStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTNodeWithErrorRecoverySymbols; |
| import org.eclipse.photran.internal.core.parser.ASTSubroutineStmtNode; |
| import org.eclipse.photran.internal.core.parser.ASTSubroutineSubprogramNode; |
| import org.eclipse.photran.internal.core.parser.ASTVisitor; |
| import org.eclipse.photran.internal.core.parser.GenericASTVisitor; |
| import org.eclipse.photran.internal.core.parser.IASTListNode; |
| import org.eclipse.photran.internal.core.parser.IProgramUnit; |
| import org.eclipse.photran.internal.core.preferences.FortranPreferences; |
| import org.eclipse.photran.internal.core.properties.SearchPathProperties; |
| import org.eclipse.rephraserengine.core.vpg.eclipse.EclipseVPG; |
| |
| /** |
| * Photran's Virtual Program Graph. |
| * |
| * @author Jeff Overbey |
| */ |
| //public class PhotranVPG extends EclipseVPG<IFortranAST, Token, PhotranTokenRef, PhotranVPGDB, PhotranVPGLog> |
| public class PhotranVPG extends EclipseVPG<IFortranAST, Token, PhotranTokenRef> |
| { |
| private static PhotranVPG instance = null; |
| |
| public static PhotranVPG getInstance() |
| { |
| if (instance == null) |
| { |
| PhotranVPGComponentFactory locator = new PhotranVPGComponentFactory(); |
| |
| if (/*inTestingMode() ||*/ FortranPreferences.ENABLE_VPG_LOGGING.getValue()) |
| { |
| instance = new PhotranVPG(locator) |
| { |
| @Override public void debug(String message, String filename) |
| { |
| System.out.println(message + " - " + lastSegmentOfFilename(filename)); //$NON-NLS-1$ |
| } |
| }; |
| } |
| else |
| { |
| instance = new PhotranVPG(locator); |
| } |
| } |
| |
| return instance; |
| } |
| |
| public static PhotranVPGWriter getProvider() |
| { |
| return getInstance().getVPGWriter(); |
| } |
| |
| //protected PhotranVPG(PhotranVPGNodeFactory locator, PhotranVPGDB db) |
| protected PhotranVPG(PhotranVPGComponentFactory locator) |
| { |
| super(locator, Messages.PhotranVPG_PhotranIndexer, 2); |
| } |
| |
| @Override public void start() |
| { |
| if (!FortranCorePlugin.inTestingMode()) super.start(); |
| } |
| |
| public static String canonicalizeIdentifier(String identifier) |
| { |
| return identifier.trim().toLowerCase().replaceAll("[ \t\r\n]", ""); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public ArrayList<Definition> findAllModulesNamed(String name) |
| { |
| ArrayList<Definition> result = new ArrayList<Definition>(); |
| for (IFile file : findFilesThatExportModule(name)) |
| result.addAll(findModules(name, file)); |
| return result; |
| } |
| |
| private ArrayList<Definition> findModules(String name, IFile file) |
| { |
| ArrayList<Definition> result = new ArrayList<Definition>(); |
| String cname = canonicalizeIdentifier(name); |
| |
| IFortranAST ast = PhotranVPG.getInstance().acquireTransientAST(file); |
| if (ast != null) |
| { |
| for (ASTModuleStmtNode module : ast.getRoot().findAll(ASTModuleStmtNode.class)) |
| { |
| Token moduleNameToken = module.getModuleName().getModuleName(); |
| String moduleName = canonicalizeIdentifier(moduleNameToken.getText()); |
| if (moduleName.equals(cname)) |
| { |
| Definition d = getDefinitionFor(moduleNameToken.getTokenRef()); |
| if (d != null) result.add(d); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| public ArrayList<Definition> findAllExternalSubprogramsNamed(String name) |
| { |
| ArrayList<Definition> result = new ArrayList<Definition>(); |
| for (IFile file : findFilesThatExportSubprogram(name)) |
| result.addAll(findSubprograms(name, file)); |
| return result; |
| } |
| |
| private ArrayList<Definition> findSubprograms(String name, IFile file) |
| { |
| ArrayList<Definition> result = new ArrayList<Definition>(); |
| String cname = canonicalizeIdentifier(name); |
| |
| IFortranAST ast = PhotranVPG.getInstance().acquireTransientAST(file); |
| if (ast != null) |
| { |
| ASTExecutableProgramNode node = ast.getRoot(); |
| for (IProgramUnit pu : node.getProgramUnitList()) |
| { |
| PhotranTokenRef tr = attemptToMatch(cname, pu); |
| if (tr != null) |
| { |
| Definition d = getDefinitionFor(tr); |
| if (d != null) result.add(d); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| private PhotranTokenRef attemptToMatch(String cname, IProgramUnit pu) |
| { |
| if (pu instanceof ASTSubroutineSubprogramNode) |
| return attemptToMatch(cname, ((ASTSubroutineSubprogramNode)pu).getSubroutineStmt()); |
| else if (pu instanceof ASTFunctionSubprogramNode) |
| return attemptToMatch(cname, ((ASTFunctionSubprogramNode)pu).getFunctionStmt()); |
| else |
| return null; |
| } |
| |
| private PhotranTokenRef attemptToMatch(String cname, ASTSubroutineStmtNode functionStmt) |
| { |
| return attemptToMatch(cname, functionStmt.getSubroutineName().getSubroutineName()); |
| } |
| |
| private PhotranTokenRef attemptToMatch(String cname, ASTFunctionStmtNode functionStmt) |
| { |
| return attemptToMatch(cname, functionStmt.getFunctionName().getFunctionName()); |
| } |
| |
| private PhotranTokenRef attemptToMatch(String cname, Token nameToken) |
| { |
| String thisSub = canonicalizeIdentifier(nameToken.getText()); |
| if (thisSub.equals(cname)) |
| return nameToken.getTokenRef(); |
| else |
| return null; |
| } |
| |
| public static ASTNodeWithErrorRecoverySymbols findFirstErrorIn(ASTExecutableProgramNode ast) |
| { |
| class V extends ASTVisitor |
| { |
| private ASTNodeWithErrorRecoverySymbols firstError = null; |
| |
| @Override public void visitASTErrorProgramUnitNode(ASTErrorProgramUnitNode node) |
| { |
| if (firstError == null) |
| firstError = node; |
| } |
| |
| @Override public void visitASTErrorConstructNode(ASTErrorConstructNode node) |
| { |
| if (firstError == null) |
| firstError = node; |
| } |
| }; |
| |
| V v = new V(); |
| ast.accept(v); |
| return v.firstError; |
| } |
| |
| // private ArrayList<Definition> mapDefinitions(ArrayList<PhotranTokenRef> tokenRefs) |
| // { |
| // ArrayList<Definition> result = new ArrayList<Definition>(); |
| // for (PhotranTokenRef tr : tokenRefs) |
| // { |
| // Definition def = getDefinitionFor(tr); |
| // if (def != null) |
| // result.add(def); |
| // } |
| // return result; |
| // } |
| |
| public ArrayList<Definition> findAllDeclarationsInInterfacesForExternalSubprogram(String name) |
| { |
| ArrayList<Definition> result = new ArrayList<Definition>(); |
| for (IFile file : findFilesThatImportSubprogram(name)) |
| result.addAll(findInterfaceSubprograms(name, file)); |
| return result; |
| } |
| |
| private ArrayList<Definition> findInterfaceSubprograms(String name, IFile file) |
| { |
| ArrayList<Definition> result = new ArrayList<Definition>(); |
| |
| IFortranAST ast = PhotranVPG.getInstance().acquireTransientAST(file); |
| if (ast != null) |
| ast.accept(new InterfaceVisitor(result, canonicalizeIdentifier(name))); |
| |
| return result; |
| } |
| |
| private final class InterfaceVisitor extends GenericASTVisitor |
| { |
| private final ArrayList<Definition> result; |
| private final String canonicalizedName; |
| |
| public InterfaceVisitor(ArrayList<Definition> result, String canonicalizedName) |
| { |
| this.result = result; |
| this.canonicalizedName = canonicalizedName; |
| } |
| |
| @Override public void visitASTFunctionStmtNode(ASTFunctionStmtNode node) |
| { |
| addIfDefinedInInterface(attemptToMatch(canonicalizedName, node)); |
| } |
| |
| @Override public void visitASTSubroutineStmtNode(ASTSubroutineStmtNode node) |
| { |
| addIfDefinedInInterface(attemptToMatch(canonicalizedName, node)); |
| } |
| |
| private void addIfDefinedInInterface(PhotranTokenRef tr) |
| { |
| Definition def = tr == null ? null : getDefinitionFor(tr); |
| if (def != null && def.isExternalSubprogramReferenceInInterfaceBlock()) |
| result.add(def); |
| } |
| } |
| |
| public ArrayList<Definition> findAllDeclarationsInExternalStmts(String name) |
| { |
| ArrayList<Definition> result = new ArrayList<Definition>(); |
| for (IFile file : findFilesThatImportSubprogram(name)) |
| result.addAll(findExternalStmts(name, file)); |
| return result; |
| } |
| |
| private ArrayList<Definition> findExternalStmts(String name, IFile file) |
| { |
| ArrayList<Definition> result = new ArrayList<Definition>(); |
| |
| IFortranAST ast = PhotranVPG.getInstance().acquireTransientAST(file); |
| if (ast != null) |
| ast.accept(new ExternalStmtVisitor(result, canonicalizeIdentifier(name))); |
| |
| return result; |
| } |
| |
| private final class ExternalStmtVisitor extends GenericASTVisitor |
| { |
| private final ArrayList<Definition> result; |
| private final String canonicalizedName; |
| |
| public ExternalStmtVisitor(ArrayList<Definition> result, String canonicalizedName) |
| { |
| this.result = result; |
| this.canonicalizedName = canonicalizedName; |
| } |
| |
| // # R1208 |
| // <ExternalStmt> ::= |
| // <LblDef> T_EXTERNAL <ExternalNameList> T_EOS |
| // | <LblDef> T_EXTERNAL T_COLON T_COLON <ExternalNameList> T_EOS |
| // |
| // <ExternalNameList> ::= |
| // <ExternalName> |
| // | @:<ExternalNameList> T_COMMA <ExternalName> |
| |
| @Override public void visitASTExternalStmtNode(ASTExternalStmtNode node) |
| { |
| super.traverseChildren(node); |
| |
| IASTListNode<ASTExternalNameListNode> list = node.getExternalNameList(); |
| for (int i = 0; i < list.size(); i++) |
| add(attemptToMatch(canonicalizedName, list.get(i).getExternalName())); |
| } |
| |
| private void add(PhotranTokenRef tr) |
| { |
| Definition def = tr == null ? null : getDefinitionFor(tr); |
| if (def != null) // && def.getClassification().equals(Classification.EXTERNAL)) |
| result.add(def); |
| } |
| } |
| |
| private List<IFile> getOutgoingIFileDependenciesFrom(String targetFilename) |
| { |
| List<IFile> files = new LinkedList<IFile>(); |
| for (String filename : super.getOutgoingDependenciesFrom(targetFilename)) |
| { |
| IFile file = getIFileForFilename(filename); |
| if (file != null) files.add(file); |
| } |
| return files; |
| } |
| |
| private List<IFile> getIncomingIFileDependenciesTo(String targetFilename) |
| { |
| List<IFile> files = new LinkedList<IFile>(); |
| for (String filename : super.getIncomingDependenciesTo(targetFilename)) |
| files.add(getIFileForFilename(filename)); |
| return files; |
| } |
| |
| public List<IFile> findFilesThatExportSubprogram(String subprogramName) |
| { |
| return getOutgoingIFileDependenciesFrom("subprogram:" + canonicalizeIdentifier(subprogramName)); //$NON-NLS-1$ |
| } |
| |
| public List<IFile> findFilesThatImportSubprogram(String subprogramName) |
| { |
| return getIncomingIFileDependenciesTo("subprogram:" + canonicalizeIdentifier(subprogramName)); //$NON-NLS-1$ |
| } |
| |
| public List<IFile> findFilesThatExportModule(String moduleName) |
| { |
| return getOutgoingIFileDependenciesFrom("module:" + canonicalizeIdentifier(moduleName)); //$NON-NLS-1$ |
| } |
| |
| public List<IFile> findFilesThatImportModule(String moduleName) |
| { |
| return getIncomingIFileDependenciesTo("module:" + canonicalizeIdentifier(moduleName)); //$NON-NLS-1$ |
| } |
| |
| public List<IFile> findFilesThatUseCommonBlock(String commonBlockName) |
| { |
| // The unnamed common block is stored with the empty name as its name |
| if (commonBlockName == null) commonBlockName = ""; //$NON-NLS-1$ |
| |
| return getIncomingIFileDependenciesTo("common:" + canonicalizeIdentifier(commonBlockName)); //$NON-NLS-1$ |
| } |
| |
| public Iterable<String> listAllModules() |
| { |
| return listAllDependentFilenamesStartingWith("module:"); //$NON-NLS-1$ |
| } |
| |
| public Iterable<String> listAllSubprograms() |
| { |
| return listAllDependentFilenamesStartingWith("subprogram:"); //$NON-NLS-1$ |
| } |
| |
| public Iterable<String> listAllCommonBlocks() |
| { |
| return listAllFilenamesWithDependentsStartingWith("common:"); //$NON-NLS-1$ |
| } |
| |
| private Iterable<String> listAllDependentFilenamesStartingWith(String prefix) |
| { |
| /* |
| * When there is a module "module1" declared in module1.f90, the VPG |
| * will contain a dependency |
| * |
| * module:module1 -----depends-on-----> module1.f90 |
| * |
| * So we can determine all modules by searching the list of dependent |
| * filenames. Note that this will include every module that is |
| * declared, even if it is never used. |
| * |
| * This same procedure works for external subprograms as well. |
| */ |
| |
| TreeSet<String> result = new TreeSet<String>(); |
| for (String name : listAllDependentFilenames()) |
| if (name.startsWith(prefix)) |
| result.add(name.substring(prefix.length())); |
| for (String name : listAllFilenamesWithDependents()) |
| if (name.startsWith(prefix)) |
| result.add(name.substring(prefix.length())); |
| return result; |
| } |
| |
| private Iterable<String> listAllFilenamesWithDependentsStartingWith(String prefix) |
| { |
| /* |
| * When there is a common block "common1" declared in common1.f90, the VPG |
| * will contain a dependency |
| * |
| * common1.f90 -----depends-on-----> common:common1 |
| * |
| * So we can determine all commons by searching the list of filenames |
| * with dependencies. |
| */ |
| |
| TreeSet<String> result = new TreeSet<String>(); |
| for (String name : listAllFilenamesWithDependents()) |
| if (name.startsWith(prefix)) |
| result.add(name.substring(prefix.length())); |
| return result; |
| } |
| |
| public Definition getDefinitionFor(PhotranTokenRef tokenRef) |
| { |
| return tokenRef.getAnnotation(AnnotationType.DEFINITION_ANNOTATION_TYPE); |
| } |
| |
| public Type getTypeFor(PhotranTokenRef tokenRef) |
| { |
| return tokenRef.getAnnotation(AnnotationType.TYPE_ANNOTATION_TYPE); |
| } |
| |
| public Visibility getVisibilityFor(Definition def, ScopingNode visibilityInScope) |
| { |
| PhotranTokenRef targetScope = visibilityInScope.getRepresentativeToken(); |
| |
| for (PhotranTokenRef privateScope : def.getTokenRef().followOutgoing(EdgeType.DEFINITION_IS_PRIVATE_IN_SCOPE_EDGE_TYPE)) |
| if (privateScope.equals(targetScope)) |
| return Visibility.PRIVATE; |
| |
| return Visibility.PUBLIC; |
| } |
| |
| public PhotranTokenRef getModuleTokenRef(String moduleName) |
| { |
| String filename = "module:" + canonicalizeIdentifier(moduleName); //$NON-NLS-1$ |
| PhotranTokenRef tokenRef = getVPGNode(filename, 0, 0); |
| //System.err.println("getModuleTokenRef(" + moduleName + ") returning " + db.getAnnotation(tokenRef, MODULE_TOKENREF_ANNOTATION_TYPE)); |
| return tokenRef.getAnnotation(AnnotationType.MODULE_TOKENREF_ANNOTATION_TYPE); |
| } |
| |
| public List<Definition> getModuleSymbolTable(String moduleName) |
| { |
| return getProvider().getModuleSymbolTable(moduleName); |
| } |
| |
| |
| |
| |
| |
| @Override |
| public String describeEdgeType(int edgeType) |
| { |
| return getProvider().describeEdgeType(edgeType); |
| } |
| |
| @Override |
| public String describeAnnotationType(int annotationType) |
| { |
| return getProvider().describeAnnotationType(annotationType); |
| } |
| |
| |
| |
| |
| public boolean doesProjectHaveRefactoringEnabled(IFile file) |
| { |
| if (FortranCorePlugin.inTestingMode()) return true; |
| |
| String vpgEnabledProperty = new SearchPathProperties().getProperty( |
| file, |
| SearchPathProperties.ENABLE_VPG_PROPERTY_NAME); |
| return vpgEnabledProperty != null && vpgEnabledProperty.equals("true"); //$NON-NLS-1$ |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| @Override public String getSourceCodeFromAST(IFortranAST ast) |
| { |
| return ast.getRoot().toString(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| @Override |
| public boolean shouldProcessFile(IFile file) |
| { |
| return FortranCorePlugin.hasFortranContentType(file.getName()); |
| } |
| |
| @Override |
| public boolean shouldProcessProject(IProject project) |
| { |
| try |
| { |
| if (!project.isAccessible()) return false; |
| if (!project.hasNature(FProjectNature.F_NATURE_ID)) return false; |
| return FortranCorePlugin.inTestingMode() || new SearchPathProperties().getProperty(project, SearchPathProperties.ENABLE_VPG_PROPERTY_NAME).equals("true"); //$NON-NLS-1$ |
| } |
| catch (CoreException e) |
| { |
| return false; // Project may be closed, or something like that; see Bug 369094 |
| } |
| } |
| |
| public String describeWhyCannotProcessProject(IProject project) |
| { |
| try |
| { |
| if (!project.isAccessible()) |
| return Messages.bind(Messages.PhotranVPG_ProjectIsNotAccessible, project.getName()); |
| else if (!project.hasNature(FProjectNature.F_NATURE_ID)) |
| return Messages.bind(Messages.PhotranVPG_ProjectIsNotAFortranProject, project.getName()); |
| else if (!new SearchPathProperties().getProperty(project, SearchPathProperties.ENABLE_VPG_PROPERTY_NAME).equals("true")) //$NON-NLS-1$ |
| return Messages.bind(Messages.PhotranVPG_AnalysisRefactoringNotEnabled, project.getName()); |
| else |
| return null; |
| } |
| catch (CoreException e) |
| { |
| return e.getLocalizedMessage(); |
| } |
| } |
| |
| public String describeWhyCannotProcessFile(IFile file) |
| { |
| if (file.getProject() == null) |
| return Messages.bind(Messages.PhotranVPG_FileIsNotInAFortranProject, file.getName()); |
| else if (!shouldProcessProject(file.getProject())) |
| return describeWhyCannotProcessProject(file.getProject()); |
| else if (!shouldProcessFile(file)) |
| return Messages.bind( |
| Messages.PhotranVPG_NotAFortranSourceFile, |
| file.getName(), |
| file.getFileExtension()); |
| else |
| return null; |
| } |
| |
| @Override |
| public boolean isVirtualFile(String filename) |
| { |
| return filename.startsWith("module:") || filename.startsWith("common:") || filename.startsWith("subprogram:"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| |
| |
| |
| |
| |
| @Override |
| public IFortranAST parse(final String filename) |
| { |
| return ((PhotranVPGWriter)getVPGWriter()).parse(filename); |
| } |
| } |