| /*=============================================================================# |
| # Copyright (c) 2008, 2021 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.r.core.model; |
| |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.CoreException; |
| |
| import org.eclipse.statet.jcommons.collections.ImCollections; |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| |
| import org.eclipse.statet.internal.r.core.FilteredFrame; |
| import org.eclipse.statet.internal.r.core.RCorePlugin; |
| import org.eclipse.statet.internal.r.core.RProjectNature; |
| import org.eclipse.statet.internal.r.core.sourcemodel.RModelManagerImpl; |
| import org.eclipse.statet.ltk.model.core.element.SourceElement; |
| import org.eclipse.statet.ltk.model.core.element.SourceUnitModelInfo; |
| import org.eclipse.statet.ltk.model.core.element.WorkspaceSourceUnit; |
| import org.eclipse.statet.r.core.RProject; |
| import org.eclipse.statet.r.core.RProjects; |
| import org.eclipse.statet.r.core.rsource.ast.RAstNode; |
| |
| |
| /** |
| * R LTK model |
| */ |
| @NonNullByDefault |
| public final class RModel { |
| |
| |
| public static final String R_TYPE_ID= "R"; //$NON-NLS-1$ |
| |
| |
| public static final RElementName GLOBAL_ENV_NAME= RElementName.create(RElementName.SCOPE_SEARCH_ENV, ".GlobalEnv"); //$NON-NLS-1$ |
| |
| |
| public static final String BUILDPATH_PROBLEM_MARKER= "org.eclipse.statet.r.resourceMarkers.BuildpathProblem"; //$NON-NLS-1$ |
| |
| public static final String R_MODEL_PROBLEM_MARKER= "org.eclipse.statet.r.resourceMarkers.RModelProblem"; //$NON-NLS-1$ |
| |
| |
| /** |
| * @return the manager for the R model |
| */ |
| public static RModelManager getRModelManager() { |
| return RCorePlugin.getInstance().getRModelManager(); |
| } |
| |
| public static @Nullable RSourceUnitModelInfo getRModelInfo(final @Nullable SourceUnitModelInfo modelInfo) { |
| if (modelInfo != null) { |
| if (modelInfo instanceof RSourceUnitModelInfo) { |
| return (RSourceUnitModelInfo)modelInfo; |
| } |
| for (final Object aAttachment : modelInfo.getAttachments()) { |
| if (aAttachment instanceof RSourceUnitModelInfo) { |
| return (RSourceUnitModelInfo)aAttachment; |
| } |
| } |
| } |
| return null; |
| } |
| |
| |
| public static @Nullable RSourceFrame searchFrame(@Nullable RAstNode node) { |
| while (node != null) { |
| final List<Object> attachments= node.getAttachments(); |
| for (final Object attachment : attachments) { |
| if (attachment instanceof RSourceFrame) { |
| return (RSourceFrame)attachment; |
| } |
| } |
| node= node.getRParent(); |
| } |
| return null; |
| } |
| |
| |
| private static @Nullable String checkValidPkgFrame(final RFrame frame) { |
| final RElementName elementName; |
| return (frame.getFrameType() == RFrame.PACKAGE |
| && (elementName= frame.getElementName()) != null) ? |
| elementName.getSegmentName() : |
| null; |
| } |
| |
| private static boolean isValidFrame(final RFrame frame, final @Nullable String pkgName) { |
| final String name; |
| return (pkgName == null |
| || ((name= checkValidPkgFrame(frame)) != null |
| && name.equals(pkgName) )); |
| } |
| |
| private static boolean isValidFrame(final RFrame frame, final @Nullable Set<String> pkgNames) { |
| final String name; |
| return (pkgNames == null |
| || ((name= checkValidPkgFrame(frame)) != null |
| && pkgNames.contains(name) )); |
| } |
| |
| public static List<RFrame> createDirectFrameList(final RFrame frame, |
| final @Nullable RElementName expliciteScope) { |
| final ArrayList<RFrame> list= new ArrayList<>(); |
| final String pkgName= (expliciteScope != null && RElementName.isPackageFacetScopeType(expliciteScope.getType())) ? |
| expliciteScope.getSegmentName() : null; |
| int idx= 0; |
| if (isValidFrame(frame, pkgName)) { |
| list.add(frame); |
| } |
| while (idx < list.size()) { |
| final List<? extends RFrame> ps= list.get(idx++).getPotentialParents(); |
| for (final RFrame parent : ps) { |
| if (isValidFrame(parent, pkgName) && !list.contains(parent)) { |
| list.add(parent); |
| } |
| } |
| } |
| return list; |
| } |
| |
| public static List<RFrame> createDirectFrameList(final RFrame frame) { |
| return createDirectFrameList(frame, null); |
| } |
| |
| public static Set<String> createImportedPackageList(final RSourceUnitModelInfo modelInfo) { |
| final Set<String> importedPackages= new HashSet<>(); |
| importedPackages.add("base"); //$NON-NLS-1$ |
| |
| if (modelInfo != null) { |
| final PackageReferences packages= modelInfo.getReferencedPackages(); |
| for (final String name : packages.getAllPackageNames()) { |
| if (packages.isImported(name)) { |
| importedPackages.add(name); |
| } |
| } |
| } |
| |
| return importedPackages; |
| } |
| |
| public static List<RFrame> createProjectFrameList(@Nullable RProject project1, |
| final RSourceUnit scope, |
| final boolean pkgImports, final boolean projectDependencies, |
| @Nullable Set<String> importedPackages, @Nullable Set<String> pkgNames) |
| throws CoreException { |
| final ArrayList<RFrame> list= new ArrayList<>(); |
| final RModelManager manager= getRModelManager(); |
| if (project1 == null && scope instanceof WorkspaceSourceUnit) { |
| if (pkgImports && importedPackages == null) { |
| importedPackages= createImportedPackageList( |
| (RSourceUnitModelInfo)scope.getModelInfo(R_TYPE_ID, RModelManagerImpl.MODEL_FILE, null )); |
| } |
| project1= RProjects.getRProject(((WorkspaceSourceUnit)scope).getResource().getProject()); |
| } |
| if (pkgImports && importedPackages == null) { |
| importedPackages= ImCollections.emptySet(); |
| } |
| |
| if (pkgNames == null) { |
| pkgNames= new HashSet<>(); |
| } |
| |
| if (project1 != null) { |
| { final RFrame frame= manager.getProjectFrame(project1); |
| if (frame != null) { |
| if (projectDependencies || (pkgImports && isValidFrame(frame, importedPackages))) { |
| final String name; |
| if ((name= checkValidPkgFrame(frame)) != null) { |
| pkgNames.add(name); |
| } |
| list.add(new FilteredFrame(frame, scope)); |
| } |
| } |
| } |
| |
| final List<RProject> projects= new ArrayList<>(); |
| try { |
| final IProject[] referencedProjects= project1.getProject().getReferencedProjects(); |
| for (final IProject referencedProject : referencedProjects) { |
| final RProject rProject= RProjectNature.getRProject(referencedProject); |
| if (rProject != null) { |
| projects.add(rProject); |
| } |
| } |
| } catch (final CoreException e) {} |
| for (int i= 0; i < projects.size(); i++) { |
| final RProject project= projects.get(i); |
| final RFrame frame= manager.getProjectFrame(project); |
| if (frame != null) { |
| if (projectDependencies || (pkgImports && isValidFrame(frame, importedPackages))) { |
| final String name; |
| if ((name= checkValidPkgFrame(frame)) != null) { |
| pkgNames.add(name); |
| } |
| list.add(frame); |
| } |
| } |
| try { |
| final IProject[] referencedProjects= project.getProject().getReferencedProjects(); |
| for (final IProject referencedProject : referencedProjects) { |
| final RProject rProject= RProjectNature.getRProject(referencedProject); |
| if (rProject != null && !projects.contains(rProject)) { |
| projects.add(rProject); |
| } |
| } |
| } catch (final CoreException e) {} |
| } |
| } |
| |
| if (pkgImports && importedPackages != null) { |
| for (final String pkgName : importedPackages) { |
| if (!pkgNames.contains(pkgName)) { |
| final RFrame frame= manager.getPkgProjectFrame(pkgName); |
| if (frame != null) { |
| list.add(frame); |
| } |
| } |
| } |
| } |
| |
| return list; |
| } |
| |
| public static List<RFrame> createProjectFrameList(final @Nullable RProject project1, |
| final RSourceUnit scope) throws CoreException { |
| return createProjectFrameList(project1, scope, true, true, null, null); |
| } |
| |
| public static List<SourceElement> searchDeclaration(final RElementAccess access, |
| final RSourceUnit su) throws CoreException { |
| assert (access != null); |
| final List<SourceElement> list= new ArrayList<>(); |
| |
| if (access.getSegmentName() == null) { |
| return list; |
| } |
| |
| final RFrame suFrame= access.getFrame(); |
| final List<RFrame> directFrames= createDirectFrameList(suFrame); |
| for (final RFrame frame : directFrames) { |
| if (checkFrame(frame, access, list)) { |
| return list; |
| } |
| } |
| final List<RFrame> projectFrames= createProjectFrameList(null, su); |
| for (final RFrame frame : projectFrames) { |
| if (checkFrame(frame, access, list)) { |
| return list; |
| } |
| } |
| return list; |
| } |
| |
| private static boolean checkFrame(final RFrame frame, final RElementAccess access, final List<SourceElement> list) { |
| final List<? extends RElement> elements= frame.getModelChildren(null); |
| for (final RElement element : elements) { |
| final RElementName name= element.getElementName(); |
| if (name != null |
| && access.getType() == name.getType() |
| && Objects.equals(access.getSegmentName(), name.getSegmentName()) |
| && element instanceof SourceElement) { |
| list.add((SourceElement)element); |
| } |
| } |
| |
| if (!list.isEmpty()) { |
| final SourceElement first= list.get(0); |
| switch (first.getElementType() & RElement.MASK_C2) { |
| case RElement.R_S4METHOD: |
| case RElement.R_GENERAL_VARIABLE: |
| return false; |
| default: |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| |
| public static @Nullable RElementName getFQElementName(final @Nullable RElement var) { |
| final List<RElementName> segments= getFQFullName(var, 0); |
| return (segments != null) ? RElementName.create(segments) : null; |
| } |
| |
| private static @Nullable List<RElementName> getFQFullName(final @Nullable RElement var, int count) { |
| if (var != null) { |
| final RElementName elementName= var.getElementName(); |
| if (elementName != null) { |
| { RElementName segment= elementName; |
| do { |
| count++; |
| segment= segment.getNextSegment(); |
| } while (segment != null); |
| } |
| List<RElementName> segments; |
| final RElementName scope= elementName.getScope(); |
| if (scope != null) { |
| if (RElementName.isScopeType(scope.getType())) { |
| segments= new ArrayList<>(count + 1); |
| segments.add(scope); |
| } |
| else { |
| segments= getFQFullName(var.getModelParent(), count); |
| } |
| } |
| else { |
| if (RElementName.isScopeType(elementName.getType())) { |
| segments= new ArrayList<>(count); |
| } |
| else { |
| segments= getFQFullName(var.getModelParent(), count); |
| } |
| } |
| if (segments != null) { |
| RElementName segment= elementName; |
| do { |
| segments.add(segment); |
| segment= segment.getNextSegment(); |
| } while (segment != null); |
| return segments; |
| } |
| } |
| } |
| return null; |
| } |
| |
| |
| private RModel() {} |
| |
| } |