diff options
Diffstat (limited to 'org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldOneToMany.java')
-rw-r--r-- | org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldOneToMany.java | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldOneToMany.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldOneToMany.java new file mode 100644 index 000000000..8f95c681f --- /dev/null +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/nd/field/FieldOneToMany.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2015, 2016 Google, Inc and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.nd.field; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.NdNode; +import org.eclipse.jdt.internal.core.nd.RawGrowableArray; + +/** + * Holds the 1 side of a 1..n relationship between two objects. FieldNodePointer and FieldBackPointer fields always go + * together in pairs. + */ +public class FieldOneToMany<T extends NdNode> implements IDestructableField, IRefCountedField, IField { + private int offset; + public Class<T> targetType; + public final Class<? extends NdNode> localType; + private final RawGrowableArray backPointerArray; + FieldManyToOne<?> forwardPointer; + + public interface Visitor<T> { + public void visit(int index, T toVisit); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private FieldOneToMany(Class<? extends NdNode> localType, FieldManyToOne<? extends NdNode> forwardPointer, + int inlineElements) { + this.localType = localType; + + if (forwardPointer != null) { + if (forwardPointer.backPointer != null && forwardPointer.backPointer != this) { + throw new IllegalArgumentException( + "Attempted to construct a FieldBackPointer referring to a forward pointer that is already in use" //$NON-NLS-1$ + + " by another field"); //$NON-NLS-1$ + } + forwardPointer.targetType = (Class)localType; + this.targetType = (Class)forwardPointer.localType; + forwardPointer.backPointer = this; + } + this.forwardPointer = forwardPointer; + this.backPointerArray = new RawGrowableArray(inlineElements); + } + + /** + * Creates a {@link FieldOneToMany} using the given builder. It will hold the many side of a one-to-many + * relationship with nodeType. + * + * @param builder builder that is being used to construct the struct containing this field + * @param forwardPointer field of the model object which holds the one side of this one-to-many relationship + * @param inlineElementCount number of inline elements. If this is nonzero, space for this number elements is + * preallocated and reserved in the header. The first few elements inserted will be stored here. For relationships + * which will usually have more than a certain number of participants, using a small number of inline elements will + * offer a performance improvement. For relationships that will normally be empty, this should be 0. + * @return the newly constructed backpointer field + */ + public static <T extends NdNode, B extends NdNode> FieldOneToMany<T> create(StructDef<B> builder, + FieldManyToOne<B> forwardPointer, int inlineElementCount) { + FieldOneToMany<T> result = new FieldOneToMany<T>(builder.getStructClass(), forwardPointer, + inlineElementCount); + builder.add(result); + builder.addDestructableField(result); + builder.addRefCountedField(result); + return result; + } + + public static <T extends NdNode, B extends NdNode> FieldOneToMany<T> create(StructDef<B> builder, + FieldManyToOne<B> forwardPointer) { + return create(builder, forwardPointer, 0); + } + + public void accept(Nd nd, long address, Visitor<T> visitor) { + int size = size(nd, address); + + for (int idx = 0; idx < size; idx++) { + visitor.visit(idx, get(nd, address, idx)); + } + } + + public List<T> asList(Nd nd, long address) { + final List<T> result = new ArrayList<>(size(nd, address)); + + accept(nd, address, new Visitor<T>() { + @Override + public void visit(int index, T toVisit) { + result.add(toVisit); + } + }); + + return result; + } + + public boolean isEmpty(Nd nd, long address) { + return this.backPointerArray.isEmpty(nd, address + this.offset); + } + + public int size(Nd nd, long address) { + return this.backPointerArray.size(nd, address + this.offset); + } + + public T get(Nd nd, long address, int index) { + long nextPointer = this.backPointerArray.get(nd, address + this.offset, index); + + return NdNode.load(nd, nextPointer, this.targetType); + } + + /** + * Removes the given index from the list. If another element is swapped into the removed element's + * location, that element's index will be updated. The removed element itself will not be modified. The + * caller is responsible for nulling out the pointer and updating its index if necessary. + * <p> + * Not intended to be called by clients. The normal way to remove something from a backpointer list is + * by calling {@link FieldManyToOne#put}, which performs the appropriate removals automatically. + */ + void remove(Nd nd, long address, int index) { + long swappedElement = this.backPointerArray.remove(nd, address + this.offset, index); + + if (swappedElement != 0) { + this.forwardPointer.adjustIndex(nd, swappedElement, index); + } + } + + /** + * Addss the given forward pointer to the list and returns the insertion index. This should not be invoked + * directly by clients. The normal way to insert into a backpointer list is to assign a forward pointer. + */ + int add(Nd nd, long address, long value) { + return this.backPointerArray.add(nd, address + this.offset, value); + } + + /** + * Returns the record size of the back pointer list + */ + public int getRecordSize() { + return this.backPointerArray.getRecordSize(); + } + + public void ensureCapacity(Nd nd, long address, int capacity) { + long arrayAddress = address + this.offset; + this.backPointerArray.ensureCapacity(nd, arrayAddress, capacity); + } + + @Override + public void destruct(Nd nd, long address) { + long arrayAddress = address + this.offset; + int size = size(nd, address); + + boolean isOwner = this.forwardPointer.pointsToOwner; + for (int idx = 0; idx < size; idx++) { + long target = this.backPointerArray.get(nd, arrayAddress, idx); + + this.forwardPointer.clearedByBackPointer(nd, target); + + if (isOwner) { + nd.scheduleDeletion(target); + } + } + + this.backPointerArray.destruct(nd, arrayAddress); + } + + public int getCapacity(Nd nd, long address) { + return this.backPointerArray.getCapacity(nd, address + this.offset); + } + + @Override + public boolean hasReferences(Nd nd, long address) { + // If this field owns the objects it points to, don't treat the incoming pointers as ref counts + if (this.forwardPointer.pointsToOwner) { + return false; + } + return !isEmpty(nd, address); + } + + @Override + public void setOffset(int offset) { + this.offset = offset; + } +} |