| /*=============================================================================# |
| # Copyright (c) 2008, 2019 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.internal.r.core.sourcemodel; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| |
| import org.eclipse.statet.jcommons.text.core.BasicTextRegion; |
| import org.eclipse.statet.jcommons.text.core.TextRegion; |
| |
| import org.eclipse.statet.ltk.ast.core.AstNode; |
| import org.eclipse.statet.ltk.model.core.elements.IModelElement; |
| import org.eclipse.statet.ltk.model.core.elements.ISourceStructElement; |
| import org.eclipse.statet.r.core.model.ArgsDefinition; |
| import org.eclipse.statet.r.core.model.IRClass; |
| import org.eclipse.statet.r.core.model.IRClassExtension; |
| import org.eclipse.statet.r.core.model.IRElement; |
| import org.eclipse.statet.r.core.model.IRFrame; |
| import org.eclipse.statet.r.core.model.IRLangSourceElement; |
| import org.eclipse.statet.r.core.model.IRMethod; |
| import org.eclipse.statet.r.core.model.IRPackageLoad; |
| import org.eclipse.statet.r.core.model.IRSlot; |
| import org.eclipse.statet.r.core.model.IRSourceUnit; |
| import org.eclipse.statet.r.core.model.RElementAccess; |
| import org.eclipse.statet.r.core.model.RElementName; |
| import org.eclipse.statet.r.core.model.RModel; |
| import org.eclipse.statet.r.core.rsource.ast.DocuComment; |
| import org.eclipse.statet.r.core.rsource.ast.FDef; |
| import org.eclipse.statet.r.core.rsource.ast.RAst; |
| |
| |
| abstract class RSourceElementByElementAccess |
| implements IRLangSourceElement, IModelElement.Filter { |
| |
| |
| static final class RPkgImport extends RSourceElementByElementAccess implements IRPackageLoad { |
| |
| |
| public RPkgImport(final IRLangSourceElement parent, final ElementAccess access) { |
| super(parent, IRElement.R_PACKAGE_LOAD, access); |
| } |
| |
| |
| @Override |
| public TextRegion getDocumentationRange() { |
| return null; |
| } |
| |
| |
| @Override |
| public boolean hasModelChildren(final IModelElement.Filter filter) { |
| return false; |
| } |
| |
| @Override |
| public final List<? extends IRLangSourceElement> getModelChildren(final IModelElement.Filter filter) { |
| return RSourceElements.NO_R_SOURCE_CHILDREN; |
| } |
| |
| @Override |
| public boolean hasSourceChildren(final IModelElement.Filter filter) { |
| return false; |
| } |
| |
| @Override |
| public List<? extends IRLangSourceElement> getSourceChildren(final IModelElement.Filter filter) { |
| return RSourceElements.NO_R_SOURCE_CHILDREN; |
| } |
| |
| } |
| |
| static abstract class DocuCommentableElement extends RSourceElementByElementAccess { |
| |
| |
| public DocuCommentableElement(final IRLangSourceElement parent, final int elementType, |
| final ElementAccess defAccess) { |
| super(parent, elementType, defAccess); |
| } |
| |
| |
| private DocuComment docu; |
| |
| |
| void setDocu(final DocuComment docu) { |
| this.docu= docu; |
| } |
| |
| @Override |
| public final DocuComment getDocumentationRange() { |
| return this.docu; |
| } |
| |
| } |
| |
| static final class RMethod extends DocuCommentableElement implements IRMethod, IBuildSourceFrameElement { |
| |
| |
| private List<? extends IRLangSourceElement> sourceChildrenProtected= RSourceElements.NO_R_SOURCE_CHILDREN; |
| private List<? extends IRLangSourceElement> modelChildrenProtected; |
| private final BuildSourceFrame envir; |
| |
| private final FDef fDefNode; |
| private ArgsDefinition args; |
| |
| |
| public RMethod(final IRLangSourceElement parent, final BuildSourceFrame envir, final FDef fdefNode) { |
| super(parent, IRElement.R_COMMON_FUNCTION, null); |
| this.envir= envir; |
| this.fDefNode= fdefNode; |
| } |
| |
| void complete(final int type, final ElementAccess defAccess, final ArgsDefinition args) { |
| this.type= type; |
| setAccess(defAccess); |
| this.args= args; |
| } |
| |
| void complete(final AnonymousAccess defAccess, final ArgsDefinition args) { |
| setAccess(defAccess); |
| this.args= args; |
| } |
| |
| |
| public RMethod(final IRLangSourceElement parent, final int type, final ElementAccess access, final BuildSourceFrame envir) { |
| super(parent, type, access); |
| this.envir= envir; |
| this.fDefNode= null; |
| } |
| |
| public void complete(final ArgsDefinition args) { |
| this.args= args; |
| } |
| |
| @Override |
| public void setSourceChildren(final List<? extends IRLangSourceElement> children) { |
| this.sourceChildrenProtected= children; |
| } |
| |
| @Override |
| public BuildSourceFrame getBuildFrame() { |
| return this.envir; |
| } |
| |
| |
| public FDef getFDefNode() { |
| return this.fDefNode; |
| } |
| |
| @Override |
| public ArgsDefinition getArgsDefinition() { |
| return this.args; |
| } |
| |
| |
| @Override |
| public boolean hasModelChildren(final IModelElement.Filter filter) { |
| if (this.modelChildrenProtected == null) { |
| this.modelChildrenProtected= this.envir.getModelChildren(this); |
| } |
| return RSourceElements.hasChildren(this.modelChildrenProtected, filter); |
| } |
| |
| @Override |
| public List<? extends IRLangSourceElement> getModelChildren(final IModelElement.Filter filter) { |
| if (this.modelChildrenProtected == null) { |
| this.modelChildrenProtected= this.envir.getModelChildren(this); |
| } |
| return RSourceElements.getChildren(this.modelChildrenProtected, filter); |
| } |
| |
| @Override |
| public boolean hasSourceChildren(final IModelElement.Filter filter) { |
| return RSourceElements.hasChildren(this.sourceChildrenProtected, filter); |
| } |
| |
| @Override |
| public List<? extends IRLangSourceElement> getSourceChildren(final IModelElement.Filter filter) { |
| return RSourceElements.getChildren(this.sourceChildrenProtected, filter); |
| } |
| |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> T getAdapter(final Class<T> adapterType) { |
| if (adapterType == IRFrame.class) { |
| return (T) this.envir; |
| } |
| if (adapterType == FDef.class) { |
| return (T) this.fDefNode; |
| } |
| return super.getAdapter(adapterType); |
| } |
| |
| } |
| |
| static final class RClass extends DocuCommentableElement implements IRClass, IBuildSourceFrameElement { |
| |
| |
| private static final List<String> NO_PARENTS= Collections.emptyList(); |
| |
| |
| private List<? extends IRLangSourceElement> sourceChildrenProtected= RSourceElements.NO_R_SOURCE_CHILDREN; |
| private List<? extends IRLangSourceElement> modelChildrenProtected; |
| private final BuildSourceFrame envir; |
| |
| private List<String> superClassesTypeNames= NO_PARENTS; |
| private List<String> superClassesTypeNamesProtected= NO_PARENTS; |
| |
| |
| public RClass(final IRLangSourceElement parent, final ElementAccess defAccess, final BuildSourceFrame envir) { |
| super(parent, IRElement.R_S4CLASS, defAccess); |
| this.envir= envir; |
| } |
| |
| public void addSuperClasses(final String[] typeNames) { |
| if (this.superClassesTypeNames == NO_PARENTS) { |
| int count= 0; |
| for (final String name : typeNames) { |
| if (name != null) { |
| count++; |
| } |
| } |
| if (count == 0) { |
| return; |
| } |
| this.superClassesTypeNames= new ArrayList<>(count); |
| this.superClassesTypeNamesProtected= Collections.unmodifiableList(this.superClassesTypeNames); |
| } |
| for (final String name : typeNames) { |
| if (name != null && !this.superClassesTypeNames.contains(name)) { |
| this.superClassesTypeNames.add(name); |
| } |
| } |
| } |
| |
| @Override |
| public void setSourceChildren(final List<? extends IRLangSourceElement> children) { |
| this.sourceChildrenProtected= children; |
| } |
| |
| @Override |
| public BuildSourceFrame getBuildFrame() { |
| return this.envir; |
| } |
| |
| |
| @Override |
| public List<String> getExtendedClassNames() { |
| return this.superClassesTypeNamesProtected; |
| } |
| |
| |
| @Override |
| public boolean hasModelChildren(final IModelElement.Filter filter) { |
| if (this.modelChildrenProtected == null) { |
| this.modelChildrenProtected= this.envir.getModelChildren(this); |
| } |
| return RSourceElements.hasChildren(this.modelChildrenProtected, filter); |
| } |
| |
| @Override |
| public List<? extends IRLangSourceElement> getModelChildren(final IModelElement.Filter filter) { |
| if (this.modelChildrenProtected == null) { |
| this.modelChildrenProtected= this.envir.getModelChildren(this); |
| } |
| return RSourceElements.getChildren(this.modelChildrenProtected, filter); |
| } |
| |
| @Override |
| public boolean hasSourceChildren(final IModelElement.Filter filter) { |
| return RSourceElements.hasChildren(this.sourceChildrenProtected, filter); |
| } |
| |
| @Override |
| public List<? extends IRLangSourceElement> getSourceChildren(final IModelElement.Filter filter) { |
| return RSourceElements.getChildren(this.sourceChildrenProtected, filter); |
| } |
| |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> T getAdapter(final Class<T> adapterType) { |
| if (adapterType == IRFrame.class) { |
| return (T) this.envir; |
| } |
| return super.getAdapter(adapterType); |
| } |
| |
| } |
| |
| static final class RClassExt extends RSourceElementByElementAccess implements IRClassExtension, IBuildSourceFrameElement { |
| |
| |
| private List<? extends IRLangSourceElement> sourceChildrenProtected= RSourceElements.NO_R_SOURCE_CHILDREN; |
| private List<? extends IRLangSourceElement> modelChildrenProtected; |
| private final BuildSourceFrame envir; |
| |
| private final String extCommand; |
| private String extTypeName; |
| |
| |
| public RClassExt(final IRLangSourceElement parent, |
| final ElementAccess defAccess, final BuildSourceFrame envir, final String command) { |
| super(parent, IRElement.R_S4CLASS_EXTENSION, defAccess); |
| this.envir= envir; |
| this.extCommand= command; |
| } |
| |
| public void complete(final String extTypeName) { |
| this.extTypeName= extTypeName; |
| } |
| |
| @Override |
| public void setSourceChildren(final List<? extends IRLangSourceElement> children) { |
| this.sourceChildrenProtected= children; |
| } |
| |
| @Override |
| public BuildSourceFrame getBuildFrame() { |
| return this.envir; |
| } |
| |
| |
| @Override |
| public String getExtCommand() { |
| return this.extCommand; |
| } |
| |
| @Override |
| public String getExtTypeName() { |
| return this.extTypeName; |
| } |
| |
| @Override |
| public TextRegion getDocumentationRange() { |
| return null; |
| } |
| |
| |
| @Override |
| public boolean hasModelChildren(final IModelElement.Filter filter) { |
| if (this.modelChildrenProtected == null) { |
| this.modelChildrenProtected= this.envir.getModelChildren(this); |
| } |
| return RSourceElements.hasChildren(this.modelChildrenProtected, filter); |
| } |
| |
| @Override |
| public List<? extends IRLangSourceElement> getModelChildren(final IModelElement.Filter filter) { |
| if (this.modelChildrenProtected == null) { |
| this.modelChildrenProtected= this.envir.getModelChildren(this); |
| } |
| return RSourceElements.getChildren(this.modelChildrenProtected, filter); |
| } |
| |
| @Override |
| public boolean hasSourceChildren(final IModelElement.Filter filter) { |
| return RSourceElements.hasChildren(this.sourceChildrenProtected, filter); |
| } |
| |
| @Override |
| public List<? extends IRLangSourceElement> getSourceChildren(final IModelElement.Filter filter) { |
| return RSourceElements.getChildren(this.sourceChildrenProtected, filter); |
| } |
| |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> T getAdapter(final Class<T> adapterType) { |
| if (adapterType == IRFrame.class) { |
| return (T) this.envir; |
| } |
| return super.getAdapter(adapterType); |
| } |
| |
| } |
| |
| static final class RVariable extends DocuCommentableElement { |
| |
| |
| public RVariable(final IRLangSourceElement parent, final int elementType, final ElementAccess defAccess) { |
| super(parent, elementType, defAccess); |
| } |
| |
| |
| @Override |
| public boolean hasModelChildren(final IModelElement.Filter filter) { |
| return false; |
| } |
| |
| @Override |
| public final List<? extends IRLangSourceElement> getModelChildren(final IModelElement.Filter filter) { |
| return RSourceElements.NO_R_SOURCE_CHILDREN; |
| } |
| |
| @Override |
| public boolean hasSourceChildren(final IModelElement.Filter filter) { |
| return false; |
| } |
| |
| @Override |
| public List<? extends IRLangSourceElement> getSourceChildren(final IModelElement.Filter filter) { |
| return RSourceElements.NO_R_SOURCE_CHILDREN; |
| } |
| |
| } |
| |
| static final class RDataFrame extends DocuCommentableElement { |
| |
| |
| private List<RElementAccess> columns; |
| |
| |
| public RDataFrame(final IRLangSourceElement parent, final int elementType, |
| final List<SubNamedPartSyntacticElementAccess> columns) { |
| super(parent, elementType, null); |
| } |
| |
| |
| @Override |
| public boolean hasModelChildren(final IModelElement.Filter filter) { |
| return false; |
| } |
| |
| @Override |
| public final List<? extends IRLangSourceElement> getModelChildren(final IModelElement.Filter filter) { |
| return RSourceElements.NO_R_SOURCE_CHILDREN; |
| } |
| |
| @Override |
| public boolean hasSourceChildren(final IModelElement.Filter filter) { |
| return false; |
| } |
| |
| @Override |
| public List<? extends IRLangSourceElement> getSourceChildren(final IModelElement.Filter filter) { |
| return RSourceElements.NO_R_SOURCE_CHILDREN; |
| } |
| |
| } |
| |
| static final class RSlot extends RSourceElementByElementAccess implements IRSlot { |
| |
| |
| private String typeName; |
| private String prototypeCode; |
| |
| |
| public RSlot(final IRLangSourceElement parent, final ElementAccess defAccess) { |
| super(parent, IRElement.R_S4SLOT, defAccess); |
| } |
| |
| void completeType(final String name) { |
| this.typeName= name; |
| } |
| |
| |
| @Override |
| public String getTypeName() { |
| return this.typeName; |
| } |
| |
| @Override |
| public TextRegion getDocumentationRange() { |
| return null; |
| } |
| |
| |
| @Override |
| public boolean hasModelChildren(final IModelElement.Filter filter) { |
| return false; |
| } |
| |
| @Override |
| public final List<? extends IRLangSourceElement> getModelChildren(final IModelElement.Filter filter) { |
| return RSourceElements.NO_R_SOURCE_CHILDREN; |
| } |
| |
| @Override |
| public boolean hasSourceChildren(final IModelElement.Filter filter) { |
| return false; |
| } |
| |
| @Override |
| public List<? extends IRLangSourceElement> getSourceChildren(final IModelElement.Filter filter) { |
| return RSourceElements.NO_R_SOURCE_CHILDREN; |
| } |
| |
| } |
| |
| |
| private final IRLangSourceElement parent; |
| private RElementAccess access; |
| int type; |
| int occurrenceCount; |
| |
| |
| public RSourceElementByElementAccess(final IRLangSourceElement parent, final int elementType, final ElementAccess defAccess) { |
| this.parent= parent; |
| this.type= elementType; |
| setAccess(defAccess); |
| } |
| |
| |
| protected void setAccess(final AnonymousAccess access) { |
| if (access != null) { |
| this.access= access; |
| } |
| } |
| |
| protected void setAccess(final ElementAccess access) { |
| if (access != null) { |
| access.modelElement= this; |
| this.access= access; |
| } |
| } |
| |
| @Override |
| public final String getModelTypeId() { |
| return RModel.R_TYPE_ID; |
| } |
| |
| public final RElementAccess getAccess() { |
| return this.access; |
| } |
| |
| @Override |
| public boolean include(final IModelElement element) { |
| return (element == this); |
| } |
| |
| @Override |
| public final IRElement getModelParent() { |
| final List<? extends IRElement> elements= this.access.getFrame().getModelElements(); |
| for (final IRElement element : elements) { |
| if (element.hasModelChildren(this)) { |
| return element; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public final ISourceStructElement getSourceParent() { |
| return this.parent; |
| } |
| |
| @Override |
| public final IRSourceUnit getSourceUnit() { |
| return this.parent.getSourceUnit(); |
| } |
| |
| @Override |
| public final int getElementType() { |
| return this.type; |
| } |
| |
| @Override |
| public final RElementName getElementName() { |
| return this.access; |
| } |
| |
| @Override |
| public final String getId() { |
| final String name= getElementName().getDisplayName(); |
| final StringBuilder sb= new StringBuilder(name.length() + 10); |
| sb.append(Integer.toHexString(this.type & MASK_C2)); |
| sb.append(':'); |
| sb.append(name); |
| sb.append('#'); |
| sb.append(this.occurrenceCount); |
| return sb.toString(); |
| } |
| |
| @Override |
| public final boolean exists() { |
| return this.parent.exists(); |
| } |
| |
| @Override |
| public final boolean isReadOnly() { |
| return this.parent.isReadOnly(); |
| } |
| |
| |
| @Override |
| public final TextRegion getSourceRange() { |
| return this.access.getNode(); |
| } |
| |
| @Override |
| public final TextRegion getNameSourceRange() { |
| final RElementAccess access= this.access.getLastSegment(); |
| if (access.getNameNode() != null) { |
| return RAst.getElementNameRegion(access.getNameNode()); |
| } |
| else { |
| return new BasicTextRegion(access.getNode().getStartOffset()); |
| } |
| } |
| |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> T getAdapter(final Class<T> adapterType) { |
| if (adapterType == AstNode.class) { |
| return (T) this.access.getNode(); |
| } |
| if (adapterType == RElementAccess.class) { |
| return (T) this.access; |
| } |
| return null; |
| } |
| |
| |
| @Override |
| public int hashCode() { |
| return (this.type & MASK_C2) * getElementName().hashCode() + this.occurrenceCount; |
| } |
| |
| @Override |
| public boolean equals(final Object obj) { |
| if (!(obj instanceof RSourceElementByElementAccess)) { |
| return false; |
| } |
| final RSourceElementByElementAccess other= (RSourceElementByElementAccess) obj; |
| return ((this.type & MASK_C2) == (other.type & MASK_C2)) |
| && (this.occurrenceCount == other.occurrenceCount) |
| && ( ((this.type & MASK_C1) == C1_SOURCE) || (getSourceParent().equals(other.getSourceParent())) ) |
| && (getElementName().equals(other.getElementName())); |
| } |
| |
| |
| @Override |
| public String toString() { |
| final StringBuilder sb= new StringBuilder(getClass().getSimpleName()); |
| sb.append(" (RSourceElementByElementAccess)"); //$NON-NLS-1$ |
| final RElementName elementName= getElementName(); |
| if (elementName != null) { |
| sb.append(' ').append(elementName); |
| } |
| else { |
| sb.append(" <unnamed>"); //$NON-NLS-1$ |
| } |
| |
| return sb.toString(); |
| } |
| |
| } |