blob: b13e8e0a16c36e7c0a7fa03359a0c0f1326bf808 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2016 Google, Inc and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Stefan Xenos (Google) - Initial implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core.nd;
import org.eclipse.jdt.internal.core.nd.db.Database;
import org.eclipse.jdt.internal.core.nd.db.IndexException;
import org.eclipse.jdt.internal.core.nd.field.FieldShort;
import org.eclipse.jdt.internal.core.nd.field.StructDef;
/**
* This is a basic polymorphic node in the network database. Pointers to NdNode or any of their
* subclasses will be resolved to the correct subclass of NdNode such that the correct version of an
* overloaded method will be invoked.
*/
public abstract class NdNode extends NdStruct implements IDestructable {
public static final FieldShort NODE_TYPE;
@SuppressWarnings("hiding")
public static final StructDef<NdNode> type;
static {
type = StructDef.create(NdNode.class, NdStruct.type);
NODE_TYPE = type.addShort();
type.done();
}
/**
* Load a node from the specified address in the given database. Return null if a node cannot
* be loaded.
*
* @param nd The {@link Nd} from which to load the node.
* @param address The address of the node in the given {@link Nd}.
* @return The {@link NdNode} at the specified location or null if a node cannot be loaded.
* @When there is a problem reading the given {@link Nd}'s Database
*/
public static NdNode load(Nd nd, long address) {
if (address == 0) {
return null;
}
try {
return nd.getNode(address, NODE_TYPE.get(nd, address));
} catch (IndexException e) {
// Add metadata to the exception describing where we obtained the node type from
nd.describeProblem().addProblemAddress(NODE_TYPE, address).attachTo(e);
throw e;
}
}
@SuppressWarnings("unchecked")
public static <T extends INdStruct> T load(Nd nd, long address, StructDef<T> typeToLoad) {
if (address == 0) {
return null;
}
NdNode result;
try {
// Polymorphic types (that subclass NdNode) store a header with their type ID
result = nd.getNode(address, NODE_TYPE.get(nd, address));
} catch (IndexException e) {
// Add metadata to the exception describing where we obtained the node type from
nd.describeProblem().addProblemAddress(NODE_TYPE, address).attachTo(e);
throw e;
}
Class<T> clazz = typeToLoad.getStructClass();
if (!clazz.isAssignableFrom(result.getClass())) {
throw nd.describeProblem()
.addProblemAddress(NODE_TYPE, address)
.build("Found wrong data type at address " + address + ". Expected a subclass of " + //$NON-NLS-1$//$NON-NLS-2$
clazz + " but found " + result.getClass()); //$NON-NLS-1$
}
return (T) result;
}
/**
* Invokes the destructor on this node and frees up its memory
*/
public final void delete() {
getNd().delete(this.address);
}
protected NdNode(Nd nd, long address) {
super(nd, address);
}
protected NdNode(Nd nd) {
super(nd, 0);
Database db = nd.getDB();
short nodeType = nd.getNodeType(getClass());
ITypeFactory<? extends NdNode> factory1 = nd.getTypeFactory(nodeType);
this.address = db.malloc(factory1.getRecordSize(), (short)(Database.POOL_FIRST_NODE_TYPE + nodeType));
NODE_TYPE.put(nd, this.address, nodeType);
}
/**
* Return a value to uniquely identify the node within the factory that is responsible for loading
* instances of this node from the {@link Nd}.
* <b>
*/
public short getNodeType() {
return this.nd.getNodeType(getClass());
}
@Override
public final long getAddress() {
return this.address;
}
public final long getBindingID() {
return this.address;
}
@Override
public final boolean equals(Object obj) {
if (obj == this)
return true;
if (obj instanceof NdNode) {
NdNode other = (NdNode) obj;
return getNd() == other.getNd() && this.address == other.address;
}
return super.equals(obj);
}
@Override
public final int hashCode() {
return (int) (this.address >> Database.BLOCK_SIZE_DELTA_BITS);
}
/**
* Return an value to globally identify the given node within the given linkage. This value
* can be used for comparison with other {@link NdNode}s.
*/
public static int getNodeId(int linkageID, int nodeType) {
return (linkageID << 16) | (nodeType & 0xffff);
}
/**
* Convenience method for fetching a byte from the database.
* @param offset Location of the byte.
* @return a byte from the database.
*/
protected byte getByte(long offset) {
return getDB().getByte(offset);
}
/**
* Returns the bit at the specified offset in a bit vector.
* @param bitVector Bits.
* @param offset The position of the desired bit.
* @return the bit at the specified offset.
*/
protected static boolean getBit(int bitVector, int offset) {
int mask = 1 << offset;
return (bitVector & mask) != 0;
}
/**
* Dispose this {@link NdNode}. Subclasses should extend this method to perform any high-level node-specific cleanup.
* This will be invoked prior to disposing the fields. Implementations must invoke their parent's destruct method
* and should not destruct the fields.
* <p>
* If an external object wants to destroy a node, they should invoke {@link NdNode#delete} rather than this
* method.
*/
@Override
public void destruct() {
// Nothing to do by default. Subclasses will provide an implementation if necessary.
}
}