Skip to content

Commit

Permalink
refactor: mutable collections handles parent and fires events
Browse files Browse the repository at this point in the history
  • Loading branch information
pvojtechovsky committed May 26, 2018
1 parent 95e2bdd commit f1697df
Show file tree
Hide file tree
Showing 8 changed files with 548 additions and 175 deletions.
Expand Up @@ -29,6 +29,8 @@
public class QualifiedNameComparator implements Comparator<CtElement>, Serializable {
private static final long serialVersionUID = 1L;

public static final QualifiedNameComparator INSTANCE = new QualifiedNameComparator();

@Override
public int compare(CtElement o1, CtElement o2) {
try {
Expand Down
115 changes: 37 additions & 78 deletions src/main/java/spoon/support/reflect/code/CtBlockImpl.java
Expand Up @@ -22,16 +22,15 @@
import spoon.reflect.code.CtStatement;
import spoon.reflect.code.CtStatementList;
import spoon.reflect.declaration.CtConstructor;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.ParentNotInitializedException;
import spoon.reflect.path.CtRole;
import spoon.reflect.visitor.CtVisitor;
import spoon.reflect.visitor.Filter;
import spoon.reflect.visitor.Query;
import spoon.support.reflect.declaration.CtElementImpl;
import spoon.support.util.EmptyIterator;
import spoon.support.util.ModelList;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

Expand All @@ -42,15 +41,34 @@ public class CtBlockImpl<R> extends CtStatementImpl implements CtBlock<R> {
private static final long serialVersionUID = 1L;

@MetamodelPropertyField(role = CtRole.STATEMENT)
private List<CtStatement> statements = emptyList();
private final ModelList<CtStatement> statements = new ModelList<CtStatement>() {
private static final long serialVersionUID = 1L;
@Override
protected CtElement getOwner() {
return CtBlockImpl.this;
}
@Override
protected CtRole getRole() {
return STATEMENT;
}
@Override
protected int getDefaultCapacity() {
return BLOCK_STATEMENTS_CONTAINER_DEFAULT_CAPACITY;
}
@Override
protected void onSizeChanged(int newSize) {
if (isImplicit() && (newSize > 1 || newSize == 0)) {
setImplicit(false);
}
}
};

public void accept(CtVisitor visitor) {
visitor.visitCtBlock(this);
}

@Override
public List<CtStatement> getStatements() {
ensureModifiableStatementsList();
return this.statements;
}

Expand Down Expand Up @@ -86,14 +104,9 @@ public <T extends CtStatementList> T insertBegin(CtStatementList statements) {
getStatements().get(0).insertAfter(statements);
return (T) this;
}
ensureModifiableStatementsList();
for (CtStatement statement : statements.getStatements()) {
statement.setParent(this);
this.addStatement(0, statement);
}
if (isImplicit() && this.statements.size() > 1) {
setImplicit(false);
}
List<CtStatement> copy = new ArrayList<>(statements.getStatements());
statements.setStatements(null);
this.statements.addAll(0, copy);
return (T) this;
}

Expand All @@ -103,28 +116,23 @@ public <T extends CtStatementList> T insertBegin(CtStatement statement) {
getStatements().get(0).insertAfter(statement);
return (T) this;
}
ensureModifiableStatementsList();
statement.setParent(this);
this.addStatement(0, statement);

if (isImplicit() && this.statements.size() > 1) {
setImplicit(false);
}
this.statements.add(0, statement);
return (T) this;
}

@Override
public <T extends CtStatementList> T insertEnd(CtStatement statement) {
ensureModifiableStatementsList();
addStatement(statement);
return (T) this;
}

@Override
public <T extends CtStatementList> T insertEnd(CtStatementList statements) {
for (CtStatement s : statements.getStatements()) {
insertEnd(s);
}
List<CtStatement> tobeInserted = new ArrayList<>(statements.getStatements());
//remove statements from the `statementsToBeInserted` before they are added to spoon model
//note: one element MUST NOT be part of two models.
statements.setStatements(null);
this.statements.addAll(this.statements.size(), tobeInserted);
return (T) this;
}

Expand Down Expand Up @@ -162,80 +170,31 @@ public <T extends CtStatementList> T insertBefore(Filter<? extends CtStatement>

@Override
public <T extends CtStatementList> T setStatements(List<CtStatement> statements) {
if (statements == null || statements.isEmpty()) {
this.statements = CtElementImpl.emptyList();
return (T) this;
}
getFactory().getEnvironment().getModelChangeListener().onListDeleteAll(this, STATEMENT, this.statements, new ArrayList<>(this.statements));
this.statements.clear();
for (CtStatement s : statements) {
addStatement(s);
}
this.statements.set(statements);
return (T) this;
}

@Override
public <T extends CtStatementList> T addStatement(CtStatement statement) {
return this.addStatement(this.statements.size(), statement);
this.statements.add(statement);
return (T) this;
}

@Override
public <T extends CtStatementList> T addStatement(int index, CtStatement statement) {
if (statement == null) {
return (T) this;
}
ensureModifiableStatementsList();
statement.setParent(this);
getFactory().getEnvironment().getModelChangeListener().onListAdd(this, STATEMENT, this.statements, index, statement);
this.statements.add(index, statement);
if (isImplicit() && this.statements.size() > 1) {
setImplicit(false);
}
return (T) this;
}

private void ensureModifiableStatementsList() {
if (this.statements == CtElementImpl.<CtStatement>emptyList()) {
this.statements = new ArrayList<>(BLOCK_STATEMENTS_CONTAINER_DEFAULT_CAPACITY);
}
}

@Override
public void removeStatement(CtStatement statement) {
if (this.statements != CtElementImpl.<CtStatement>emptyList()) {
boolean hasBeenRemoved = false;
// we cannot use a remove(statement) as it uses the equals
// and a block can have twice exactly the same statement.
for (int i = 0; i < this.statements.size(); i++) {
if (this.statements.get(i) == statement) {
getFactory().getEnvironment().getModelChangeListener().onListDelete(this, STATEMENT, statements, i, statement);
this.statements.remove(i);
hasBeenRemoved = true;
break;
}
}

// in case we use it with a statement manually built
if (!hasBeenRemoved) {
getFactory().getEnvironment().getModelChangeListener().onListDelete(this, STATEMENT, statements, statements.indexOf(statement), statement);
this.statements.remove(statement);
}

if (isImplicit() && statements.size() == 0) {
setImplicit(false);
}
}
this.statements.remove(statement);
}

@Override
public Iterator<CtStatement> iterator() {
if (getStatements().isEmpty()) {
return EmptyIterator.instance();
}
// we have to both create a defensive object and an unmodifiable list
// with only Collections.unmodifiableList you can modify the defensive object
// with only new ArrayList it breaks the encapsulation
return Collections.unmodifiableList(new ArrayList<>(getStatements())).iterator();
return this.statements.iterator();
}

@Override
Expand Down
47 changes: 22 additions & 25 deletions src/main/java/spoon/support/reflect/code/CtJavaDocImpl.java
Expand Up @@ -19,70 +19,67 @@
import spoon.reflect.annotations.MetamodelPropertyField;
import spoon.reflect.code.CtJavaDoc;
import spoon.reflect.code.CtJavaDocTag;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.path.CtRole;
import spoon.reflect.visitor.CtVisitor;
import spoon.support.util.ModelList;

import java.util.ArrayList;
import java.util.List;

import static spoon.reflect.path.CtRole.COMMENT_TAG;

public class CtJavaDocImpl extends CtCommentImpl implements CtJavaDoc {

@MetamodelPropertyField(role = CtRole.COMMENT_TAG)
List<CtJavaDocTag> tags = new ArrayList<>();
private final ModelList<CtJavaDocTag> tags = new ModelList<CtJavaDocTag>() {
@Override
protected CtElement getOwner() {
return CtJavaDocImpl.this;
}
@Override
protected CtRole getRole() {
return CtRole.COMMENT_TAG;
}
@Override
protected int getDefaultCapacity() {
return 2;
}
};;

public CtJavaDocImpl() {
super(CommentType.JAVADOC);
}

@Override
public List<CtJavaDocTag> getTags() {
return new ArrayList<>(tags);
return tags;
}

@Override
public <E extends CtJavaDoc> E setTags(List<CtJavaDocTag> tags) {
if (tags == null) {
return (E) this;
}
getFactory().getEnvironment().getModelChangeListener().onListDeleteAll(this, COMMENT_TAG, this.tags, new ArrayList<>(this.tags));
this.tags = new ArrayList<>();
for (CtJavaDocTag tag : tags) {
this.addTag(tag);
}
this.tags.set(tags);
return (E) this;
}

@Override
public <E extends CtJavaDoc> E addTag(CtJavaDocTag tag) {
if (tag != null) {
tag.setParent(this);
getFactory().getEnvironment().getModelChangeListener().onListAdd(this, COMMENT_TAG, tags, tag);
tags.add(tag);
}
this.tags.add(tag);
return (E) this;
}

@Override
public <E extends CtJavaDoc> E addTag(int index, CtJavaDocTag tag) {
tag.setParent(this);
getFactory().getEnvironment().getModelChangeListener().onListAdd(this, COMMENT_TAG, tags, index, tag);
tags.add(index, tag);
this.tags.add(index, tag);
return (E) this;
}

@Override
public <E extends CtJavaDoc> E removeTag(int index) {
getFactory().getEnvironment().getModelChangeListener().onListDelete(this, COMMENT_TAG, tags, index, tags.get(index));
tags.remove(index);
this.tags.remove(index);
return (E) this;
}

@Override
public <E extends CtJavaDoc> E removeTag(CtJavaDocTag tag) {
getFactory().getEnvironment().getModelChangeListener().onListDelete(this, COMMENT_TAG, tags, tags.indexOf(tag), tag);
tags.remove(tag);
this.tags.remove(tag);
return (E) this;
}

Expand Down

0 comments on commit f1697df

Please sign in to comment.