/*
* Copyright (c) 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.qt.core.pdom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.eclipse.cdt.internal.core.pdom.db.BTree;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.cdt.internal.core.pdom.db.IBTreeComparator;
import org.eclipse.cdt.internal.core.pdom.db.IBTreeVisitor;
import org.eclipse.cdt.internal.core.pdom.db.IString;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMName;
import org.eclipse.core.runtime.CoreException;
/**
* A data structure for storing lists of PDOMNames that are indexed by a String key.
* This is equivalent to the java type Map<String, List<PDOMName>>.
*/
@SuppressWarnings("restriction")
public class QtPDOMNameIndex {
private final QtPDOMLinkage qtLinkage;
private final Database db;
private final BTree btree;
// Entries in the index look like:
// struct Entry {
// record key;
// record head;
// };
//
// Elements in the list for each entry look like:
// struct ListNode {
// record pdomName;
// record next;
// };
public QtPDOMNameIndex(QtPDOMLinkage qtLinkage, long ptr) throws CoreException {
this.qtLinkage = qtLinkage;
this.db = qtLinkage.getDB();
this.btree = new BTree(db, ptr, new StringKeyComparator());
}
public Collection<PDOMName> get(String key) throws CoreException {
Finder finder = new Finder(key);
btree.accept(finder);
if (finder.headRec == 0)
return Collections.emptyList();
List<PDOMName> names = new ArrayList<PDOMName>();
for(long node = db.getRecPtr(finder.headRec); node != 0; node = db.getRecPtr(node + Database.PTR_SIZE))
names.add(new PDOMName(qtLinkage, db.getRecPtr(node)));
return names;
}
public void add(String key, PDOMName name) throws CoreException {
IString dbKey = db.newString(key);
// Construct a temporary entry and try to insert it into the tree.
long tmpEntry = db.malloc(2 * Database.PTR_SIZE);
db.putRecPtr(tmpEntry, dbKey.getRecord());
long entry = btree.insert(tmpEntry);
// If the new entry was inserted into the tree, then we need to allocate new
// memory for the list node. If the tree already had an entry for this key, then
// we need to release the string that was provisionally allocated as the key, but
// can reuse the memory for the list node.
long node = 0;
if (entry == tmpEntry)
node = db.malloc(Database.PTR_SIZE);
else {
dbKey.delete();
node = tmpEntry;
}
// The registration can now be put into the new list node.
db.putRecPtr(node, name.getRecord());
// Finally, the new list node should be inserted before the current head.
long head = db.getRecPtr(entry + Database.PTR_SIZE);
db.putRecPtr(node + Database.PTR_SIZE, head);
db.putRecPtr(entry + Database.PTR_SIZE, node);
}
public void remove(String key, PDOMName name) throws CoreException {
Finder finder = new Finder(key);
btree.accept(finder);
if (finder.headRec == 0)
return;
long qmlRec = name.getRecord();
// Walk the list to find this record. If found then update the previous node to
// point to the one after node.
long prev = finder.headRec;
for(long node = db.getRecPtr(prev); node != 0; node = db.getRecPtr(prev)) {
long rec = db.getRecPtr(node);
if (rec == qmlRec) {
long next = db.getRecPtr(node + Database.PTR_SIZE);
db.putRecPtr(prev, next);
db.free(node);
break;
}
prev = node + Database.PTR_SIZE;
}
// The lifetime of the binding is managed elsewhere so don't delete it here. We
// are just maintaining the consistency of this index.
}
private class StringKeyComparator implements IBTreeComparator {
@Override
public int compare(long record1, long record2) throws CoreException {
long lhsRec = db.getRecPtr(record1);
long rhsRec = db.getRecPtr(record2);
IString lhs = lhsRec == 0 ? null : db.getString(lhsRec);
IString rhs = rhsRec == 0 ? null : db.getString(rhsRec);
if (lhs == null)
return rhs == null ? 0 : -1;
return rhs == null ? 1 : lhs.compare(rhs, true);
}
}
private class Finder implements IBTreeVisitor {
private final String key;
public Long headRec = 0L;
public Finder(String key) {
this.key = key;
}
@Override
public int compare(long rhsRecord) throws CoreException {
long keyRec = db.getRecPtr(rhsRecord);
return keyRec == 0 ? 1 : db.getString(keyRec).compare(key, true);
}
@Override
public boolean visit(long record) throws CoreException {
headRec = record + Database.PTR_SIZE;
return false;
}
}
}