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