blob: 1ccf24765120824970f998847f0999a244a62915 [file] [log] [blame]
/*=============================================================================#
# 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();
}
}