| /*=============================================================================# |
| # Copyright (c) 2008, 2020 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.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| 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.LtkModelUtils; |
| import org.eclipse.statet.ltk.model.core.element.LtkModelElement; |
| import org.eclipse.statet.ltk.model.core.element.LtkModelElementFilter; |
| import org.eclipse.statet.r.core.model.ArgsDefinition; |
| import org.eclipse.statet.r.core.model.RElement; |
| import org.eclipse.statet.r.core.model.RElementAccess; |
| import org.eclipse.statet.r.core.model.RElementName; |
| import org.eclipse.statet.r.core.model.RFrame; |
| import org.eclipse.statet.r.core.model.RLangClass; |
| import org.eclipse.statet.r.core.model.RLangClassExtension; |
| import org.eclipse.statet.r.core.model.RLangMethod; |
| import org.eclipse.statet.r.core.model.RLangPackageLoad; |
| import org.eclipse.statet.r.core.model.RLangSlot; |
| import org.eclipse.statet.r.core.model.RLangSourceElement; |
| import org.eclipse.statet.r.core.model.RModel; |
| import org.eclipse.statet.r.core.model.RSourceUnit; |
| 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.RAsts; |
| |
| |
| @NonNullByDefault |
| abstract class RSourceElementByElementAccess |
| implements RLangSourceElement, LtkModelElementFilter<LtkModelElement<?>> { |
| |
| |
| static final class RPkgImport extends RSourceElementByElementAccess implements RLangPackageLoad<RLangSourceElement> { |
| |
| |
| public RPkgImport(final RLangSourceElement parent, final ElementAccess access) { |
| super(parent, RElement.R_PACKAGE_LOAD, access); |
| } |
| |
| |
| @Override |
| public @Nullable TextRegion getDocumentationRange() { |
| return null; |
| } |
| |
| |
| @Override |
| public boolean hasModelChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return false; |
| } |
| |
| @Override |
| public final List<? extends RLangSourceElement> getModelChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return RSourceModel.NO_R_SOURCE_CHILDREN; |
| } |
| |
| @Override |
| public boolean hasSourceChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return false; |
| } |
| |
| @Override |
| public List<? extends RLangSourceElement> getSourceChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return RSourceModel.NO_R_SOURCE_CHILDREN; |
| } |
| |
| } |
| |
| static abstract class DocuCommentableElement extends RSourceElementByElementAccess { |
| |
| |
| private @Nullable DocuComment docu; |
| |
| |
| public DocuCommentableElement(final RLangSourceElement parent, final int elementType, |
| final @Nullable ElementAccess defAccess) { |
| super(parent, elementType, defAccess); |
| } |
| |
| |
| void setDocu(final DocuComment docu) { |
| this.docu= docu; |
| } |
| |
| @Override |
| public final @Nullable DocuComment getDocumentationRange() { |
| return this.docu; |
| } |
| |
| } |
| |
| static abstract class DocuCommentableEnvirElement extends DocuCommentableElement |
| implements BuildSourceFrameElement { |
| |
| private List<? extends RLangSourceElement> sourceChildrenProtected= RSourceModel.NO_R_SOURCE_CHILDREN; |
| private @Nullable List<? extends RLangSourceElement> modelChildrenProtected; |
| private final BuildSourceFrame envir; |
| |
| protected DocuCommentableEnvirElement(final RLangSourceElement parent, final BuildSourceFrame envir, |
| final int elementType, final @Nullable ElementAccess defAccess) { |
| super(parent, elementType, defAccess); |
| this.envir= envir; |
| } |
| |
| |
| @Override |
| public void setSourceChildren(final List<? extends RLangSourceElement> children) { |
| this.sourceChildrenProtected= children; |
| } |
| |
| @Override |
| public BuildSourceFrame getBuildFrame() { |
| return this.envir; |
| } |
| |
| |
| @Override |
| public boolean hasModelChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| List<? extends RLangSourceElement> children= this.modelChildrenProtected; |
| if (children == null) { |
| children= this.envir.getModelChildren(this); |
| this.modelChildrenProtected= children; |
| } |
| return LtkModelUtils.<RLangSourceElement>hasChildren(children, filter); |
| } |
| |
| @Override |
| public List<? extends RLangSourceElement> getModelChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| List<? extends RLangSourceElement> children= this.modelChildrenProtected; |
| if (children == null) { |
| children= this.envir.getModelChildren(this); |
| this.modelChildrenProtected= children; |
| } |
| return LtkModelUtils.<RLangSourceElement>getChildren(children, filter); |
| } |
| |
| @Override |
| public boolean hasSourceChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return LtkModelUtils.<RLangSourceElement>hasChildren(this.sourceChildrenProtected, filter); |
| } |
| |
| @Override |
| public List<? extends RLangSourceElement> getSourceChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return LtkModelUtils.<RLangSourceElement>getChildren(this.sourceChildrenProtected, filter); |
| } |
| |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> @Nullable T getAdapter(final Class<T> adapterType) { |
| if (adapterType == RFrame.class) { |
| return (T) this.envir; |
| } |
| return super.getAdapter(adapterType); |
| } |
| |
| } |
| |
| static final class RMethod extends DocuCommentableEnvirElement implements RLangMethod<RLangSourceElement> { |
| |
| |
| private final FDef fDefNode; |
| private @Nullable ArgsDefinition args; |
| |
| |
| public RMethod(final RLangSourceElement parent, final BuildSourceFrame envir, final FDef fdefNode) { |
| super(parent, envir, RElement.R_COMMON_FUNCTION, null); |
| this.fDefNode= fdefNode; |
| } |
| |
| void complete(final int type, final ElementAccess defAccess, final @Nullable 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 RLangSourceElement parent, final int type, final ElementAccess access, final BuildSourceFrame envir) { |
| super(parent, envir, type, access); |
| this.fDefNode= null; |
| } |
| |
| public void complete(final ArgsDefinition args) { |
| this.args= args; |
| } |
| |
| |
| public FDef getFDefNode() { |
| return this.fDefNode; |
| } |
| |
| @Override |
| public @Nullable ArgsDefinition getArgsDefinition() { |
| return this.args; |
| } |
| |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> @Nullable T getAdapter(final Class<T> adapterType) { |
| if (adapterType == FDef.class) { |
| return (T) this.fDefNode; |
| } |
| return super.getAdapter(adapterType); |
| } |
| |
| } |
| |
| static final class RClass extends DocuCommentableEnvirElement implements RLangClass<RLangSourceElement> { |
| |
| |
| private static final List<String> NO_PARENTS= Collections.emptyList(); |
| |
| |
| private List<String> superClassesTypeNames= NO_PARENTS; |
| private List<String> superClassesTypeNamesProtected= NO_PARENTS; |
| |
| |
| public RClass(final RLangSourceElement parent, final ElementAccess defAccess, final BuildSourceFrame envir) { |
| super(parent, envir, RElement.R_S4CLASS, defAccess); |
| } |
| |
| public void addSuperClasses(final @Nullable 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 List<String> getExtendedClassNames() { |
| return this.superClassesTypeNamesProtected; |
| } |
| |
| |
| } |
| |
| static final class RClassExt extends RSourceElementByElementAccess implements RLangClassExtension<RLangSourceElement>, BuildSourceFrameElement { |
| |
| |
| private List<? extends RLangSourceElement> sourceChildrenProtected= RSourceModel.NO_R_SOURCE_CHILDREN; |
| private @Nullable List<? extends RLangSourceElement> modelChildrenProtected; |
| private final BuildSourceFrame envir; |
| |
| private final String extCommand; |
| private @Nullable String extTypeName; |
| |
| |
| public RClassExt(final RLangSourceElement parent, |
| final ElementAccess defAccess, final BuildSourceFrame envir, final String command) { |
| super(parent, RElement.R_S4CLASS_EXTENSION, defAccess); |
| this.envir= envir; |
| this.extCommand= command; |
| } |
| |
| public void complete(final @Nullable String extTypeName) { |
| this.extTypeName= extTypeName; |
| } |
| |
| @Override |
| public void setSourceChildren(final List<? extends RLangSourceElement> children) { |
| this.sourceChildrenProtected= children; |
| } |
| |
| @Override |
| public BuildSourceFrame getBuildFrame() { |
| return this.envir; |
| } |
| |
| |
| @Override |
| public String getExtCommand() { |
| return this.extCommand; |
| } |
| |
| @Override |
| public @Nullable String getExtTypeName() { |
| return this.extTypeName; |
| } |
| |
| @Override |
| public @Nullable TextRegion getDocumentationRange() { |
| return null; |
| } |
| |
| |
| |
| @Override |
| public boolean hasModelChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| List<? extends RLangSourceElement> children= this.modelChildrenProtected; |
| if (children == null) { |
| children= this.envir.getModelChildren(this); |
| this.modelChildrenProtected= children; |
| } |
| return LtkModelUtils.<RLangSourceElement>hasChildren(children, filter); |
| } |
| |
| @Override |
| public List<? extends RLangSourceElement> getModelChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| List<? extends RLangSourceElement> children= this.modelChildrenProtected; |
| if (children == null) { |
| children= this.envir.getModelChildren(this); |
| this.modelChildrenProtected= children; |
| } |
| return LtkModelUtils.<RLangSourceElement>getChildren(children, filter); |
| } |
| |
| @Override |
| public boolean hasSourceChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return LtkModelUtils.<RLangSourceElement>hasChildren(this.sourceChildrenProtected, filter); |
| } |
| |
| @Override |
| public List<? extends RLangSourceElement> getSourceChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return LtkModelUtils.<RLangSourceElement>getChildren(this.sourceChildrenProtected, filter); |
| } |
| |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> @Nullable T getAdapter(final Class<T> adapterType) { |
| if (adapterType == RFrame.class) { |
| return (T) this.envir; |
| } |
| return super.getAdapter(adapterType); |
| } |
| |
| } |
| |
| static final class RVariable extends DocuCommentableElement { |
| |
| |
| public RVariable(final RLangSourceElement parent, final int elementType, final ElementAccess defAccess) { |
| super(parent, elementType, defAccess); |
| } |
| |
| |
| @Override |
| public boolean hasModelChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return false; |
| } |
| |
| @Override |
| public final List<? extends RLangSourceElement> getModelChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return RSourceModel.NO_R_SOURCE_CHILDREN; |
| } |
| |
| @Override |
| public boolean hasSourceChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return false; |
| } |
| |
| @Override |
| public List<? extends RLangSourceElement> getSourceChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return RSourceModel.NO_R_SOURCE_CHILDREN; |
| } |
| |
| } |
| |
| static final class RDataFrame extends DocuCommentableElement { |
| |
| |
| private List<RElementAccess> columns; |
| |
| |
| public RDataFrame(final RLangSourceElement parent, final int elementType, |
| final List<SubNamedPartSyntacticElementAccess> columns) { |
| super(parent, elementType, null); |
| } |
| |
| |
| @Override |
| public boolean hasModelChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return false; |
| } |
| |
| @Override |
| public final List<? extends RLangSourceElement> getModelChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return RSourceModel.NO_R_SOURCE_CHILDREN; |
| } |
| |
| @Override |
| public boolean hasSourceChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return false; |
| } |
| |
| @Override |
| public List<? extends RLangSourceElement> getSourceChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return RSourceModel.NO_R_SOURCE_CHILDREN; |
| } |
| |
| } |
| |
| static final class RSlot extends RSourceElementByElementAccess implements RLangSlot<RLangSourceElement> { |
| |
| |
| private @Nullable String typeName; |
| private String prototypeCode; |
| |
| |
| public RSlot(final RLangSourceElement parent, final ElementAccess defAccess) { |
| super(parent, RElement.R_S4SLOT, defAccess); |
| } |
| |
| void completeType(final @Nullable String name) { |
| this.typeName= name; |
| } |
| |
| |
| @Override |
| public @Nullable String getTypeName() { |
| return this.typeName; |
| } |
| |
| @Override |
| public @Nullable TextRegion getDocumentationRange() { |
| return null; |
| } |
| |
| |
| @Override |
| public boolean hasModelChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return false; |
| } |
| |
| @Override |
| public final List<? extends RLangSourceElement> getModelChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return RSourceModel.NO_R_SOURCE_CHILDREN; |
| } |
| |
| @Override |
| public boolean hasSourceChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return false; |
| } |
| |
| @Override |
| public List<? extends RLangSourceElement> getSourceChildren(final @Nullable LtkModelElementFilter<? super RLangSourceElement> filter) { |
| return RSourceModel.NO_R_SOURCE_CHILDREN; |
| } |
| |
| } |
| |
| |
| private final RLangSourceElement parent; |
| private RElementAccess access; |
| int type; |
| int occurrenceCount; |
| |
| |
| public RSourceElementByElementAccess(final RLangSourceElement parent, final int elementType, |
| final @Nullable ElementAccess defAccess) { |
| this.parent= parent; |
| this.type= elementType; |
| setAccess(defAccess); |
| } |
| |
| |
| protected void setAccess(final @Nullable AnonymousAccess access) { |
| if (access != null) { |
| this.access= access; |
| } |
| } |
| |
| protected void setAccess(final @Nullable 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 LtkModelElement<?> element) { |
| return (element == this); |
| } |
| |
| @Override |
| public final @Nullable RElement<?> getModelParent() { |
| final List<? extends RElement<?>> elements= this.access.getFrame().getModelElements(); |
| for (final RElement<?> element : elements) { |
| if (element.hasModelChildren(this)) { |
| return element; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public final RLangSourceElement getSourceParent() { |
| return this.parent; |
| } |
| |
| @Override |
| public final RSourceUnit 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 RAsts.getElementNameRegion(access.getNameNode()); |
| } |
| else { |
| return new BasicTextRegion(access.getNode().getStartOffset()); |
| } |
| } |
| |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T> @Nullable 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 @Nullable 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(); |
| } |
| |
| } |