/*
* Copyright (c) 2013, 2014 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
*/
package org.eclipse.cdt.internal.core.pdom.db;
import java.util.NoSuchElementException;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.internal.core.pdom.PDOM;
import org.eclipse.cdt.internal.core.pdom.dom.IPDOMIterator;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMName;
import org.eclipse.core.runtime.CoreException;
/**
* A utility class for storing a list of external references. An external reference is
* a PDOMName that references a PDOMBinding in a different linkage. This list can be
* examined using {@link #getIterator()}.
*/
public class PDOMExternalReferencesList {
/*
* This data structure is a list of lists. The inner lists are names that should that
* should be created in the same linkage. The outer list collects the heads of the
* inner lists.
*
* This example shows this storage structure for three names in two linkages.
*
* NameA2
* ^
* |
* NameA1 NameB
* ^ ^
* | |
* record --> LinkageA --> LinkageB
*
* NameA1 and NameA2 should both be created in LinkageA, NameB should be created in LinkageB.
*
* The interface to this class flattens this storage structure so it appears as a simple
* list that can be iterated over.
*
* Continuing with the same example, the iterator behaves as though the list were:
*
* { NameA1, NameA2, NameB }
*
* This class mostly doesn't care about the inner lists. They are implemented using the
* #getNextInBinding attribute of PDOMName.
*
* This class implements the outer list as a singly linked list of "nodes". Each node stores
* the linkageId, the record of the first PDOMName in the list, and the record of the next node.
*/
private final PDOM pdom;
private final long record;
/**
* Create a new instance at the given location in the given PDOM.
*/
public PDOMExternalReferencesList(PDOM pdom, long record) throws CoreException {
this.pdom = pdom;
this.record = record;
}
/**
* Return an iterator for examining all names in the external references list. Does
* not return null.
*/
public IPDOMIterator<PDOMName> getIterator() throws CoreException {
return new Iterator(record);
}
/**
* Add the given name to this list.
*/
public void add(PDOMName name) throws CoreException {
// External references are stored in a linked list of linkages. Each node in that list
// is a list that uses the PDOMName binding list.
PDOMLinkage nameLinkage = name.getLinkage();
int nameLinkageID = nameLinkage.getLinkageID();
// Search through the nodes to find the one for the new name's linkage. Keep track of
// the record that held the last examined node so that a new node can be appended if
// needed.
long lastAddr = record;
long nodeRec = 0;
while ((nodeRec = pdom.getDB().getRecPtr(lastAddr)) != 0) {
// Each node looks like { int linkageID; recPtr nextNode; recPtr headOfList; }
int linkageID = pdom.getDB().getInt(nodeRec);
if (linkageID == nameLinkageID)
break;
lastAddr = nodeRec + Database.INT_SIZE;
}
// If there isn't already a node for this linkage, then create a new one.
if (nodeRec == 0) {
nodeRec = pdom.getDB().malloc(Database.INT_SIZE + Database.PTR_SIZE + Database.PTR_SIZE);
// Setup the new node for this linkage and initialize the list ptr with an empty list.
pdom.getDB().putInt(nodeRec, nameLinkageID);
pdom.getDB().putRecPtr(nodeRec + Database.INT_SIZE, 0);
pdom.getDB().putRecPtr(nodeRec + Database.INT_SIZE + Database.PTR_SIZE, 0);
// Finally insert the new node right after the last one that was examined.
pdom.getDB().putRecPtr(lastAddr, nodeRec);
}
// If the list is not empty then modify the first element to be right after the name that
// is being inserted.
long namerec = pdom.getDB().getRecPtr(nodeRec + Database.INT_SIZE + Database.PTR_SIZE);
if (namerec != 0) {
PDOMName first = new PDOMName(nameLinkage, namerec);
first.setPrevInBinding(name);
name.setNextInBinding(first);
}
// Finally, make the new name the first element in the list.
pdom.getDB().putRecPtr(nodeRec + Database.INT_SIZE + Database.PTR_SIZE, name.getRecord());
}
/**
* This function must be used during PDOMName deletion when the list head is being deleted.
*/
public void setFirstReference(PDOMLinkage linkage, PDOMName name) throws CoreException {
// Find the head of this linkage's list.
PDOMName head = null;
Iterator iterator = new Iterator(record);
while (head == null) {
if (iterator.next == null)
break;
if (!linkage.equals(iterator.next.getLinkage())) {
iterator.next = iterator.advance();
} else {
head = iterator.next;
}
}
// If there isn't already a node for this name's linkage then the new name can be inserted
// in the normal way.
if (head == null) {
add(name);
} else {
// Otherwise update the node's head field to point to the given name.
long nextNameRec = iterator.node + Database.INT_SIZE + Database.PTR_SIZE;
pdom.getDB().putRecPtr(nextNameRec, name == null ? 0 : name.getRecord());
}
}
private class Iterator implements IPDOMIterator<PDOMName> {
private long nodeAddr;
private long node;
private PDOMName next;
public Iterator(long record) throws CoreException {
this.nodeAddr = record;
this.node = 0;
// Initialize next by advancing to the first name.
this.next = advance();
}
@Override
public boolean hasNext() throws CoreException {
return next != null;
}
@Override
public PDOMName next() throws CoreException {
if (next == null)
throw new NoSuchElementException();
PDOMName ret = next;
next = ret.getNextInBinding();
if (next == null)
next = advance();
return ret;
}
/**
* Advance to the next linkage node that has a non-empty list of names. Return the
* PDOMName at the head of the list. This is the next name that should be returned
* from #next(). Return null if there are no more linkage nodes.
*/
private PDOMName advance() throws CoreException {
// Look through all linkage nodes to find the next one that has a non-empty
// names list.
while(true) {
// Skip over all nodes that don't have any names in their list.
long nextNameRec = 0;
while(nodeAddr != 0) {
node = pdom.getDB().getRecPtr(nodeAddr);
if (node == 0)
return null;
nextNameRec = pdom.getDB().getRecPtr(node + Database.INT_SIZE + Database.PTR_SIZE);
if (nextNameRec != 0)
break;
nodeAddr = node + Database.INT_SIZE;
}
// If nothing is found then there is no more iterating.
if (nodeAddr == 0
|| nextNameRec == 0)
return null;
// If a node is found that has a name in the list, then update this iterator to
// point to the next linkage's node (this is so the next call to advance starts
// at the right place).
nodeAddr = pdom.getDB().getRecPtr(node + Database.INT_SIZE);
// Load node's linkage and use it to return the first name in this node's list.
int linkageID = pdom.getDB().getInt(node);
PDOMLinkage linkage = pdom.getLinkage(linkageID);
if (linkage != null)
return new PDOMName(linkage, nextNameRec);
// Generate a log message about the linkageID that is no longer valid, but continue
// to the next node.
CCorePlugin.log("Could not load linkage for external reference from linkageID " + linkageID); //$NON-NLS-1$
}
}
}
}