/*
* Copyright (c) 2013, 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
*/
package org.eclipse.cdt.internal.qt.core.pdom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.internal.core.dom.parser.ITypeMarshalBuffer;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPExecution;
import org.eclipse.cdt.internal.core.pdom.PDOM;
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.dom.FindBinding;
import org.eclipse.cdt.internal.core.pdom.dom.IPDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMFile;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMGlobalScope;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMName;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode;
import org.eclipse.cdt.internal.core.pdom.dom.cpp.PDOMCPPGlobalScope;
import org.eclipse.cdt.internal.qt.core.Activator;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
public class QtPDOMLinkage extends PDOMLinkage {
private static int offsetInitializer = PDOMLinkage.RECORD_SIZE;
private static enum Field {
Version(Database.INT_SIZE, 0),
QmlRegistrationIndex(Database.PTR_SIZE, 3),
Last(0, 0);
private final int offset;
public final int version;
private Field(int sizeof, int version) {
this.offset = offsetInitializer;
this.version = version;
offsetInitializer += sizeof;
}
public long getRecord(long baseRec) {
return baseRec + offset;
}
}
// The version that has been read from/written to the persisted file.
private int version;
private final Map<IQtASTName, PDOMBinding> cache = new WeakHashMap<IQtASTName, PDOMBinding>();
public QtPDOMLinkage(PDOM pdom, long record) throws CoreException {
super(pdom, record);
version = pdom.getDB().getInt(Field.Version.getRecord(record));
}
protected QtPDOMLinkage(PDOM pdom) throws CoreException {
super(pdom, ILinkage.QT_LINKAGE_NAME, ILinkage.QT_LINKAGE_NAME.toCharArray());
// Initialize the version with whatever is current.
version = QtPDOMNodeType.VERSION;
pdom.getDB().putInt(Field.Version.getRecord(record), version);
// Initialize all BTree's to 0.
if (version >= Field.QmlRegistrationIndex.version)
pdom.getDB().putRecPtr(Field.QmlRegistrationIndex.getRecord(record), 0);
}
@Override
protected int getRecordSize() {
return Field.Last.offset;
}
public int getVersion() {
return version;
}
@Override
public String getLinkageName() {
return ILinkage.QT_LINKAGE_NAME;
}
@Override
public int getLinkageID() {
return ILinkage.QT_LINKAGE_ID;
}
@Override
public PDOMNode getNode(long record, int nodeType) throws CoreException {
return QtPDOMNodeType.load(this, nodeType, record);
}
@Override
public IBTreeComparator getIndexComparator() {
return new FindBinding.DefaultBindingBTreeComparator(this);
}
@Override
public PDOMGlobalScope getGlobalScope() {
return PDOMCPPGlobalScope.INSTANCE;
}
// IBinding#getAdapter cannot create an instance of PDOMBinding because the Linkage is required. This
// utility method uses #getAdapter to see if an instance has already been created. If not then a new
// is created and stored in the AST binding.
@Override
public PDOMBinding adaptBinding(IBinding binding, boolean includeLocal) throws CoreException {
if (binding == null)
return null;
// If a binding has already been persisted for this instance then return it now.
QtPDOMBinding pdomBinding = (QtPDOMBinding) binding.getAdapter(QtPDOMBinding.class);
if (pdomBinding != null
&& pdomBinding.getLinkage() == this)
return pdomBinding;
// If a PDOMBinding was created, then add it to the linkage before returning it.
if (pdomBinding != null) {
addChild(pdomBinding);
return pdomBinding;
}
// Otherwise fall back to looking in the C++ linkage.
return getPDOM().getLinkage(ILinkage.CPP_LINKAGE_ID).adaptBinding(binding);
}
public long getCPPRecord(IASTName cppName) throws CoreException {
if (cppName == null)
return 0;
IBinding binding = getPDOM().findBinding(cppName);
if (binding == null)
return 0;
IPDOMBinding pdomBinding = (IPDOMBinding) binding.getAdapter(IPDOMBinding.class);
if (pdomBinding == null)
return 0;
if (pdomBinding.getLinkage() == null
|| pdomBinding.getLinkage().getLinkageID() != ILinkage.CPP_LINKAGE_ID)
return 0;
return pdomBinding.getRecord();
}
/**
* Return the PDOMBinding for the given Qt name creating a new binding if needed. The
* implementation caches the result using the name instance as the key. This ensures
* one-to-one uniqueness between AST names and PDOMBindings.
* <p>
* This method is not thread-safe.
*/
public PDOMBinding getBinding(IQtASTName qtAstName) throws CoreException {
// The Qt implementation ensures uniqueness by creating only a single instance of
// the IASTName for each thing that should create a single instance in the PDOM.
// This will work as long as all Qt elements are updated at once, which is currently
// the case.
//
// I don't think this needs to be thread-safe, because things are only added from
// the single indexer task.
//
// Doug: The cache is causing out of memory conditions. Commenting out for now.
//
PDOMBinding pdomBinding = null;
pdomBinding = cache.get(qtAstName);
if (pdomBinding != null)
return pdomBinding;
// The result is cached even when null is returned.
pdomBinding = qtAstName.createPDOMBinding(this);
cache.put(qtAstName, pdomBinding);
// Only add children that are actually created.
if (pdomBinding != null)
addChild(pdomBinding);
return pdomBinding;
}
@Override
public PDOMBinding addBinding(IASTName name) throws CoreException {
// The Qt linkage is able to reference elements in other linkages. This implementation
// needs to decide if the binding associated with this name is from the Qt linkage or
// from one of those external references.
if (name == null)
return null;
if (name instanceof IQtASTName)
return getBinding((IQtASTName) name);
IBinding binding = name.getBinding();
if (binding == null)
return null;
// Use the receiving linkage by default, and override only if the binding is found to
// have a linkage with a different id.
PDOMLinkage pdomLinkage = this;
ILinkage linkage = binding.getLinkage();
if (linkage != null
&& linkage.getLinkageID() != getLinkageID())
pdomLinkage = getPDOM().getLinkage(linkage.getLinkageID());
// Handle bindings in unknown linkages as though the name is to be added to this linkage.
return (pdomLinkage == null ? this : pdomLinkage).adaptBinding(binding);
}
@Override
public void onCreateName(PDOMFile file, IASTName name, PDOMName pdomName) throws CoreException {
super.onCreateName(file, name, pdomName);
// If the new name was created for a QmlRegistration, then put it into the index.
if (name instanceof QmlTypeRegistration) {
String qobjName = ((QmlTypeRegistration) name).getQObjectName();
QtPDOMNameIndex index = getQmlRegistrationIndex();
if (index != null)
index.add(qobjName, pdomName);
}
}
@Override
public void onDeleteName(PDOMName name) throws CoreException {
// If this is a name for a QML registration, then the registration must be removed
// from the index.
PDOMBinding binding = name.getBinding();
if (binding instanceof QtPDOMQmlRegistration) {
QtPDOMNameIndex index = getQmlRegistrationIndex();
if (index != null)
index.remove(((QtPDOMQmlRegistration) binding).getQObjectName(), name);
}
super.onDeleteName(name);
}
public Collection<QtPDOMQmlRegistration> getQmlRegistrations(String qobjName) throws CoreException {
QtPDOMNameIndex index = getQmlRegistrationIndex();
if (index == null)
return Collections.emptyList();
Collection<PDOMName> names = index.get(qobjName);
if (names.isEmpty())
return Collections.emptyList();
ArrayList<QtPDOMQmlRegistration> registrations = new ArrayList<QtPDOMQmlRegistration>();
for (PDOMName name : names) {
PDOMBinding binding = name.getBinding();
if (binding instanceof QtPDOMQmlRegistration)
registrations.add((QtPDOMQmlRegistration) binding);
}
return registrations;
}
private QtPDOMNameIndex getQmlRegistrationIndex() throws CoreException {
return version >= Field.QmlRegistrationIndex.version
? new QtPDOMNameIndex(this, Field.QmlRegistrationIndex.getRecord(record))
: null;
}
@Override
public int getBindingType(IBinding binding) {
return binding instanceof QtPDOMBinding ? ((QtPDOMBinding) binding).getNodeType() : 0;
}
@Override
public PDOMBinding addTypeBinding(IBinding binding) throws CoreException {
throw new CoreException(Activator.error("Qt Linkage does not manage types")); //$NON-NLS-1$
}
@Override
public IType unmarshalType(ITypeMarshalBuffer buffer) throws CoreException {
throw new CoreException(Activator.error("Qt Linkage does not marshal types")); //$NON-NLS-1$
}
@Override
public IBinding unmarshalBinding(ITypeMarshalBuffer buffer) throws CoreException {
throw new CoreException(Activator.error("Qt Linkage does not marshal bindings")); //$NON-NLS-1$
}
@Override
public ICPPEvaluation unmarshalEvaluation(ITypeMarshalBuffer typeMarshalBuffer) throws CoreException {
throw new CoreException(Activator.error("Qt Linkage does not marshal evaluations")); //$NON-NLS-1$
}
@Override
public ICPPExecution unmarshalExecution(ITypeMarshalBuffer typeMarhsalBuffer) throws CoreException {
throw new CoreException(Activator.error("Qt Linkage does not marshal executions")); //$NON-NLS-1$
}
}