/******************************************************************************* * Copyright (c) 2005, 2015 QNX Software Systems 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: * Doug Schaefer (QNX) - Initial API and implementation * Markus Schorn (Wind River Systems) * Andrew Ferguson (Symbian) * Sergey Prigogin (Google) *******************************************************************************/ package org.eclipse.cdt.internal.core.pdom.dom; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.dom.ILinkage; import org.eclipse.cdt.core.dom.IPDOMVisitor; import org.eclipse.cdt.internal.core.index.IIndexBindingConstants; import org.eclipse.cdt.internal.core.pdom.PDOM; import org.eclipse.cdt.internal.core.pdom.db.Database; import org.eclipse.core.runtime.CoreException; /** * This is a basic node in the PDOM database. * PDOM nodes form a multi-root tree with linkages being the roots. * This class managed the parent pointer. */ public abstract class PDOMNode implements IInternalPDOMNode { private static final int FACTORY_ID = 0; private static final int NODE_TYPE = FACTORY_ID + 2; private static final int PARENT = NODE_TYPE + 2; protected static final int RECORD_SIZE = PARENT + Database.PTR_SIZE; private final PDOMLinkage fLinkage; protected final long record; private volatile long cachedParentRecord; /** * Load a node from the specified record in the given database. Return null if a node cannot * be loaded. * * @param pdom The PDOM from which to load the node. * @param record The record of the node in the given PDOM. * @return The PDOMNode at the specified location or null if a node cannot be loaded. * @throws CoreException When there is a problem reading the given pdom's Database */ public static PDOMNode load(PDOM pdom, long record) throws CoreException { if (record == 0) { return null; } Database db = pdom.getDB(); // Decode the factory id from the serialized type. If it is a valid PDOMLinkage then // use that linkage to build the node. Otherwise fall back to using this linkage. short factoryId = db.getShort(record + FACTORY_ID); short nodeType = db.getShort(record + NODE_TYPE); // For an unknown reason linkages cannot be loaded with this method. if (nodeType == IIndexBindingConstants.LINKAGE) return null; PDOMLinkage factory = pdom.getLinkage(factoryId); return factory == null ? null : factory.getNode(record, nodeType); } protected PDOMNode(PDOMLinkage linkage, long record) { fLinkage = linkage; this.record = record; } protected PDOMNode(PDOMLinkage linkage, PDOMNode parent) throws CoreException { this(linkage.getDB(), linkage, parent == null ? 0 : parent.getRecord()); } /** * For linkages, only. */ protected PDOMNode(Database db) throws CoreException { this(db, null, 0); } protected PDOMNode(Database db, PDOMLinkage linkage, long parentRec) throws CoreException { this.fLinkage = linkage; record = db.malloc(getRecordSize()); short factoryId = (short) (linkage == null ? ILinkage.NO_LINKAGE_ID : linkage.getLinkageID()); db.putShort(record + FACTORY_ID, factoryId); db.putShort(record + NODE_TYPE, (short) getNodeType()); cachedParentRecord = parentRec; db.putRecPtr(record + PARENT, parentRec); } protected Database getDB() { return fLinkage.getDB(); } public PDOM getPDOM() { return fLinkage.getPDOM(); } public PDOMLinkage getLinkage() { return fLinkage; } protected abstract int getRecordSize(); /** * Return a value to uniquely identify the node within the factory that is responsible for loading * instances of this node from the PDOM. * <b> * NOTE: For historical reasons the return value is an int. However, implementations must ensure that * the result fits in a short. The upper two bytes will be ignored. */ public abstract int getNodeType(); @Override public final long getRecord() { return record; } public final long getBindingID() { return record; } /** * Checks if <code>other</code> node is the immediate parent of this one. * @param other paternity test subject. * @return <code>true</code> if <code>other</code> node in the parent of this one. */ public boolean isChildOf(PDOMNode other) { try { return other.fLinkage == fLinkage && other.record == getParentNodeRec(); } catch (CoreException e) { return false; } } @Override public final boolean equals(Object obj) { if (obj == this) return true; if (obj instanceof PDOMNode) { PDOMNode other = (PDOMNode) obj; return getPDOM() == other.getPDOM() && record == other.record; } return super.equals(obj); } @Override public final int hashCode() { return System.identityHashCode(getPDOM()) + (int) (41 * record); } @Override public void accept(IPDOMVisitor visitor) throws CoreException { // No children here. } /** * Uniquely identifies the type of a node using the factoryId and the nodeType. This * should only be used for comparison with the result of calls to this method on other * nodes. */ public static int getNodeId(Database db, long record) throws CoreException { return getNodeId(db.getShort(record + FACTORY_ID), db.getShort(record + NODE_TYPE)); } /** * Return an value to globally identify the given node within the given linkage. This value * can be used for comparison with other PDOMNodes. */ public static int getNodeId(int linkageID, int nodeType) { return (linkageID << 16) | (nodeType & 0xffff); } /** * Return a value that identifies the node within a linkage. This value cannot be * used for global comparison because it does not contain enough information to identify * the linkage within which this id is unique. * @see #getNodeId(Database, long) */ public static int getNodeType(Database db, long record) throws CoreException { return (db.getShort(record + NODE_TYPE)); } public long getParentNodeRec() throws CoreException { if (cachedParentRecord != 0) { return cachedParentRecord; } return cachedParentRecord= getDB().getRecPtr(record + PARENT); } public PDOMNode getParentNode() throws CoreException { long parentrec = getParentNodeRec(); return parentrec != 0 ? load(getPDOM(), parentrec) : null; } public void addChild(PDOMNode child) throws CoreException { // nothing here } /** * 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) { try { return getDB().getByte(offset); } catch (CoreException e) { CCorePlugin.log(e); return 0; } } /** * Convenience method for fetching a two-byte integer number from the database. * * @param offset Location of the number * @return a number from the database. */ protected short getShort(long offset) { try { return getDB().getShort(offset); } catch (CoreException e) { CCorePlugin.log(e); return 0; } } /** * 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; } /** * Delete this PDOMNode, make sure you are actually the owner of this record! * @param linkage * @throws CoreException */ @Override public void delete(PDOMLinkage linkage) throws CoreException { getDB().free(record); } }