1 /******************************************************************************* 2 * Copyright (c) 2015, 2016 Google, Inc and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * Stefan Xenos (Google) - Initial implementation 13 *******************************************************************************/ 14 package org.eclipse.jdt.internal.core.nd.field; 15 16 import java.util.ArrayList; 17 import java.util.List; 18 19 import org.eclipse.jdt.internal.core.nd.INdStruct; 20 import org.eclipse.jdt.internal.core.nd.Nd; 21 import org.eclipse.jdt.internal.core.nd.NdNode; 22 import org.eclipse.jdt.internal.core.nd.RawGrowableArray; 23 24 /** 25 * Holds the 1 side of a 1..n relationship between two objects. FieldNodePointer and FieldBackPointer fields always go 26 * together in pairs. 27 */ 28 public class FieldOneToMany<T extends INdStruct> extends BaseField implements IDestructableField, IRefCountedField { 29 public StructDef<T> targetType; 30 public final StructDef<? extends INdStruct> localType; 31 private final RawGrowableArray backPointerArray; 32 FieldManyToOne<?> forwardPointer; 33 34 public interface Visitor<T> { visit(int index, T toVisit)35 public void visit(int index, T toVisit); 36 } 37 38 @SuppressWarnings({ "rawtypes", "unchecked" }) FieldOneToMany(StructDef<? extends INdStruct> localType, FieldManyToOne<? extends INdStruct> forwardPointer, int inlineElements)39 private FieldOneToMany(StructDef<? extends INdStruct> localType, FieldManyToOne<? extends INdStruct> forwardPointer, 40 int inlineElements) { 41 this.localType = localType; 42 43 if (forwardPointer != null) { 44 if (forwardPointer.backPointer != null && forwardPointer.backPointer != this) { 45 throw new IllegalArgumentException( 46 "Attempted to construct a FieldBackPointer referring to a forward pointer that is already in use" //$NON-NLS-1$ 47 + " by another field"); //$NON-NLS-1$ 48 } 49 forwardPointer.targetType = (StructDef) localType; 50 this.targetType = (StructDef) forwardPointer.localType; 51 forwardPointer.backPointer = this; 52 } 53 this.forwardPointer = forwardPointer; 54 setFieldName("field " + localType.getNumFields() + ", a " + getClass().getSimpleName() //$NON-NLS-1$//$NON-NLS-2$ 55 + " in struct " + localType.getStructName()); //$NON-NLS-1$ 56 this.backPointerArray = new RawGrowableArray(inlineElements); 57 } 58 59 /** 60 * Creates a {@link FieldOneToMany} using the given builder. It will hold the many side of a one-to-many 61 * relationship with nodeType. 62 * 63 * @param builder builder that is being used to construct the struct containing this field 64 * @param forwardPointer field of the model object which holds the one side of this one-to-many relationship 65 * @param inlineElementCount number of inline elements. If this is nonzero, space for this number elements is 66 * preallocated and reserved in the header. The first few elements inserted will be stored here. For relationships 67 * which will usually have more than a certain number of participants, using a small number of inline elements will 68 * offer a performance improvement. For relationships that will normally be empty, this should be 0. 69 * @return the newly constructed backpointer field 70 */ create(StructDef<B> builder, FieldManyToOne<B> forwardPointer, int inlineElementCount)71 public static <T extends INdStruct, B extends INdStruct> FieldOneToMany<T> create(StructDef<B> builder, 72 FieldManyToOne<B> forwardPointer, int inlineElementCount) { 73 FieldOneToMany<T> result = new FieldOneToMany<T>(builder, forwardPointer, inlineElementCount); 74 builder.add(result); 75 builder.addDestructableField(result); 76 builder.addRefCountedField(result); 77 return result; 78 } 79 create(StructDef<B> builder, FieldManyToOne<B> forwardPointer)80 public static <T extends INdStruct, B extends INdStruct> FieldOneToMany<T> create(StructDef<B> builder, 81 FieldManyToOne<B> forwardPointer) { 82 return create(builder, forwardPointer, 0); 83 } 84 accept(Nd nd, long address, Visitor<T> visitor)85 public void accept(Nd nd, long address, Visitor<T> visitor) { 86 int size = size(nd, address); 87 88 for (int idx = 0; idx < size; idx++) { 89 visitor.visit(idx, get(nd, address, idx)); 90 } 91 } 92 asList(Nd nd, long address)93 public List<T> asList(Nd nd, long address) { 94 final List<T> result = new ArrayList<>(size(nd, address)); 95 96 accept(nd, address, new Visitor<T>() { 97 @Override 98 public void visit(int index, T toVisit) { 99 result.add(toVisit); 100 } 101 }); 102 103 return result; 104 } 105 isEmpty(Nd nd, long address)106 public boolean isEmpty(Nd nd, long address) { 107 return this.backPointerArray.isEmpty(nd, address + this.offset); 108 } 109 size(Nd nd, long address)110 public int size(Nd nd, long address) { 111 return this.backPointerArray.size(nd, address + this.offset); 112 } 113 get(Nd nd, long address, int index)114 public T get(Nd nd, long address, int index) { 115 long nextPointer = this.backPointerArray.get(nd, address + this.offset, index); 116 117 return NdNode.load(nd, nextPointer, this.targetType); 118 } 119 getAddressOf(Nd nd, long address, int index)120 public long getAddressOf(Nd nd, long address, int index) { 121 return this.backPointerArray.get(nd, address + this.offset, index); 122 } 123 124 /** 125 * Removes the given index from the list. If another element is swapped into the removed element's 126 * location, that element's index will be updated. The removed element itself will not be modified. The 127 * caller is responsible for nulling out the pointer and updating its index if necessary. 128 * <p> 129 * Not intended to be called by clients. The normal way to remove something from a backpointer list is 130 * by calling {@link FieldManyToOne#put}, which performs the appropriate removals automatically. 131 */ remove(Nd nd, long address, int index)132 void remove(Nd nd, long address, int index) { 133 long swappedElement = this.backPointerArray.remove(nd, address + this.offset, index); 134 135 if (swappedElement != 0) { 136 this.forwardPointer.adjustIndex(nd, swappedElement, index); 137 } 138 } 139 140 /** 141 * Addss the given forward pointer to the list and returns the insertion index. This should not be invoked 142 * directly by clients. The normal way to insert into a backpointer list is to assign a forward pointer. 143 */ add(Nd nd, long address, long value)144 int add(Nd nd, long address, long value) { 145 return this.backPointerArray.add(nd, address + this.offset, value); 146 } 147 148 /** 149 * Returns the record size of the back pointer list 150 */ 151 @Override getRecordSize()152 public int getRecordSize() { 153 return this.backPointerArray.getRecordSize(); 154 } 155 ensureCapacity(Nd nd, long address, int capacity)156 public void ensureCapacity(Nd nd, long address, int capacity) { 157 long arrayAddress = address + this.offset; 158 this.backPointerArray.ensureCapacity(nd, arrayAddress, capacity); 159 } 160 161 @Override destruct(Nd nd, long address)162 public void destruct(Nd nd, long address) { 163 long arrayAddress = address + this.offset; 164 int size = size(nd, address); 165 166 boolean isOwner = this.forwardPointer.pointsToOwner; 167 for (int idx = 0; idx < size; idx++) { 168 long target = this.backPointerArray.get(nd, arrayAddress, idx); 169 170 this.forwardPointer.clearedByBackPointer(nd, target); 171 172 if (isOwner) { 173 nd.scheduleDeletion(target); 174 } 175 } 176 177 this.backPointerArray.destruct(nd, arrayAddress); 178 } 179 getCapacity(Nd nd, long address)180 public int getCapacity(Nd nd, long address) { 181 return this.backPointerArray.getCapacity(nd, address + this.offset); 182 } 183 184 @Override hasReferences(Nd nd, long address)185 public boolean hasReferences(Nd nd, long address) { 186 // If this field owns the objects it points to, don't treat the incoming pointers as ref counts 187 if (this.forwardPointer.pointsToOwner) { 188 return false; 189 } 190 return !isEmpty(nd, address); 191 } 192 } 193