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