1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 14:42:11 +02:00

Bug 422841: Initial implementation of QtIndex

This uses the new PDOMASTProcessor extension point to create a
Qt-specifc PDOMLinkage.  This initial version of the linkage only stores
QObjects and their base classes.  Later commits will fill out other
details and introduce classes that use this data for things like Content
Assistance and Codan checking.

This patch introduces the following:

1) QtIndex: This is an index that provides access to Qt-specific data.
The index is mostly a wrapper on the CDT's existing IIndex, but it
provides very specific information about the Qt elements.  The only data
that can be accessed from the QtIndex (in this patch) is a QObject and
the QObject's that appear in its base class specifier list.

2) QtPDOMLinkage: This linkage is implemenated as an extension of the
PDOMCPPLinkage.  In some cases it adds references to Qt names from the
C++ bindings.

3) Test suite: The test suite has some base classes for parsing the Qt
source code.  The only test case is for the simple functionality that is
being added in this patch -- examining the base class specifier list of
QObjects.

These areas will be extended in later patches.

Change-Id: I13fb83beb7f50cd2efb1de97b562245dc642468d
Signed-off-by: Andrew Eidsness <eclipse@jfront.com>
Reviewed-on: https://git.eclipse.org/r/19113
Tested-by: Hudson CI
Reviewed-by: Doug Schaefer <dschaefer@qnx.com>
IP-Clean: Doug Schaefer <dschaefer@qnx.com>
This commit is contained in:
Andrew Eidsness 2013-11-29 08:28:51 -05:00 committed by Doug Schaefer
parent 8610899d03
commit fccbec7b0d
34 changed files with 2196 additions and 291 deletions

View file

@ -28,6 +28,11 @@ public interface ILinkage {
*/
final static String OBJC_LINKAGE_NAME= "Objective-C"; //$NON-NLS-1$
/**
* @since 5.6
*/
final static String QT_LINKAGE_NAME= "Qt"; //$NON-NLS-1$
final static int NO_LINKAGE_ID= 0;
final static int CPP_LINKAGE_ID= 1;
final static int C_LINKAGE_ID= 2;
@ -37,6 +42,11 @@ public interface ILinkage {
*/
final static int OBJC_LINKAGE_ID= 4;
/**
* @since 5.6
*/
final static int QT_LINKAGE_ID= 5;
/**
* Additional linkage ids may be added in future.

View file

@ -20,8 +20,9 @@ public class Linkage implements ILinkage {
public static final ILinkage CPP_LINKAGE = new Linkage(CPP_LINKAGE_ID, CPP_LINKAGE_NAME);
public static final ILinkage FORTRAN_LINKAGE = new Linkage(FORTRAN_LINKAGE_ID, FORTRAN_LINKAGE_NAME);
public static final ILinkage OBJC_LINKAGE = new Linkage(OBJC_LINKAGE_ID, OBJC_LINKAGE_NAME);
public static final ILinkage QT_LINKAGE = new Linkage(QT_LINKAGE_ID, QT_LINKAGE_NAME);
private static final ILinkage[] LINKAGES= { C_LINKAGE, CPP_LINKAGE, FORTRAN_LINKAGE, OBJC_LINKAGE };
private static final ILinkage[] LINKAGES= { C_LINKAGE, CPP_LINKAGE, FORTRAN_LINKAGE, OBJC_LINKAGE, QT_LINKAGE };
private static final ILinkage[] INDEX_LINKAGES= { C_LINKAGE, CPP_LINKAGE, FORTRAN_LINKAGE };
public static final ILinkage[] getIndexerLinkages() {
@ -39,6 +40,7 @@ public class Linkage implements ILinkage {
case CPP_LINKAGE_ID: return CPP_LINKAGE_NAME;
case FORTRAN_LINKAGE_ID: return FORTRAN_LINKAGE_NAME;
case OBJC_LINKAGE_ID: return OBJC_LINKAGE_NAME;
case QT_LINKAGE_ID: return QT_LINKAGE_NAME;
}
throw new CoreException(CCorePlugin.createStatus("Unsupported linkage id: " + linkageID)); //$NON-NLS-1$
}

View file

@ -21,8 +21,7 @@
<toolChain id="cdt.managedbuild.toolchain.gnu.base"/>
<toolChain id="cdt.managedbuild.toolchain.gnu.macosx.base"/>
<toolChain id="cdt.managedbuild.toolchain.gnu.solaris.base"/>
<toolChain
id="cdt.managedbuild.toolchain.llvm.clang.macosx.base">
<toolChain id="cdt.managedbuild.toolchain.llvm.clang.macosx.base">
</toolChain>
</template>
</extension>
@ -53,12 +52,11 @@
</runtime>
</extension>
<extension
point="org.eclipse.cdt.core.tagger"
id="qt.signalslots"
name="Qt Signal/Slot Tagger">
<bindingTagger
local-id="signalslot.tagger"
class="org.eclipse.cdt.qt.internal.core.QtSignalSlotTagger">
point="org.eclipse.cdt.core.PDOMASTProcessor"
id="qt.PDOMASTProcessor"
name="Qt PDOM AST Processor">
<processor
class="org.eclipse.cdt.qt.internal.core.pdom.QtPDOMASTProcessor">
<enablement>
<with variable="projectNatures">
<iterate operator="or">
@ -66,6 +64,11 @@
</iterate>
</with>
</enablement>
</bindingTagger>
</processor>
</extension>
<extension point="org.eclipse.cdt.core.language">
<pdomLinkageFactory
id="Qt"
class="org.eclipse.cdt.qt.internal.core.pdom.PDOMQtLinkageFactory"/>
</extension>
</plugin>

View file

@ -13,11 +13,27 @@ package org.eclipse.cdt.qt.core;
*/
public class QtKeywords {
public static final String CONNECT = "connect";
public static final String DISCONNECT = "disconnect";
public static final String Q_CLASSINFO = "Q_CLASSINFO";
public static final String Q_DECLARE_FLAGS = "Q_DECLARE_FLAGS";
public static final String Q_ENUMS = "Q_ENUMS";
public static final String Q_FLAGS = "Q_FLAGS";
public static final String Q_GADGET = "Q_GADGET";
public static final String Q_INVOKABLE = "Q_INVOKABLE";
public static final String Q_OBJECT = "Q_OBJECT";
public static final String Q_PROPERTY = "Q_PROPERTY";
public static final String Q_REVISION = "Q_REVISION";
public static final String Q_SIGNAL = "Q_SIGNAL";
public static final String Q_SIGNALS = "Q_SIGNALS";
public static final String Q_SLOT = "Q_SLOT";
public static final String Q_SLOTS = "Q_SLOTS";
public static final String QMETAMETHOD = "QMetaMethod";
public static final String QML_ATTACHED_PROPERTIES = "qmlAttachedProperties";
public static final String QML_REGISTER_TYPE = "qmlRegisterType";
public static final String QML_REGISTER_UNCREATABLE_TYPE = "qmlRegisterUncreatableType";
public static final String QOBJECT = "QObject";
public static final String SIGNAL = "SIGNAL";
public static final String SIGNALS = "signals";
public static final String SLOT = "SLOT";
public static final String SLOTS = "slots";
}

View file

@ -1,11 +1,16 @@
/*
* Copyright (c) 2013 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.qt.core;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.internal.qt.core.index.QMakeProjectInfo;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.osgi.framework.BundleContext;
public class QtPlugin extends Plugin {
@ -18,62 +23,50 @@ public class QtPlugin extends Plugin {
public static final String QMAKE_ENV_PROVIDER_EXT_POINT_NAME = "qmakeEnvProvider"; //$NON-NLS-1$
public static final String QMAKE_ENV_PROVIDER_ID = ID + "." + QMAKE_ENV_PROVIDER_EXT_POINT_NAME; //$NON-NLS-1$
private static QtPlugin INSTANCE;
private static BundleContext context;
static BundleContext getContext() {
return context;
}
static QtPlugin getDefault() {
return INSTANCE;
}
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
/**
* Instances of QtIndex are cached within the session properties of the project from
* which they are created. This name is used to store the property.
*/
@Override
public void start(BundleContext bundleContext) throws Exception {
INSTANCE = this;
QtPlugin.context = bundleContext;
QMakeProjectInfo.start();
public static final QualifiedName QTINDEX_PROP_NAME = new QualifiedName(ID, "qtindex");
private static QtPlugin instance;
public static QtPlugin getDefault() {
return instance;
}
/*
* (non-Javadoc)
* @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
*/
@Override
public void stop(BundleContext bundleContext) throws Exception {
QMakeProjectInfo.stop();
QtPlugin.context = null;
INSTANCE = null;
public QtPlugin() {
instance = this;
}
public static void log(String e) {
log(IStatus.INFO, e, null);
public static IStatus info(String msg) {
return new Status(IStatus.INFO, ID, msg);
}
public static void log(Throwable e) {
public static IStatus error(String msg) {
return error(msg, null);
}
public static IStatus error(String msg, Throwable e) {
return new Status(IStatus.ERROR, ID, msg, e);
}
public static IStatus log(String e) {
return log(IStatus.INFO, e, null);
}
public static IStatus log(Throwable e) {
String msg = e.getMessage();
if (msg == null) {
log("Error", e); //$NON-NLS-1$
} else {
log("Error: " + msg, e); //$NON-NLS-1$
}
return msg == null ? log("Error", e) : log("Error: " + msg, e);
}
public static void log(String message, Throwable e) {
Throwable nestedException;
if (e instanceof CModelException && (nestedException = ((CModelException)e).getException()) != null) {
e = nestedException;
}
log(IStatus.ERROR, message, e);
public static IStatus log(String message, Throwable e) {
return log(IStatus.ERROR, message, e);
}
public static void log(int code, String msg, Throwable e) {
getDefault().getLog().log(new Status(code, ID, msg, e));
public static IStatus log(int code, String msg, Throwable e) {
IStatus status = new Status(code, ID, msg, e);
instance.getLog().log(status);
return status;
}
}

View file

@ -0,0 +1,20 @@
/*
* Copyright (c) 2013 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.qt.core.index;
import org.eclipse.cdt.core.dom.ast.IBinding;
/**
* Base interface for things that are accessed from the {@link QtIndex}.
*/
public interface IQElement {
/**
* Returns the IBinding from the CDT index for the receiver element.
*/
public IBinding getBinding();
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2013 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.qt.core.index;
import java.util.List;
/**
* A class that inherits the Qt QObject and contains an expansion of Q_OBJECT. This
* provides a handle for retrieving signals, slots, and other Qt-related elements.
* @see IQMethod
*/
public interface IQObject extends IQElement {
/**
* Returns the name of the class.
*/
public String getName();
/**
* Returns a list of the QObject's that are bases of this class.
* E.g. in:
* <pre>
* class T {};
* class B1 : public QObject { Q_OBJECT };
* class B2 : public QObject { Q_OBJECT };
* class B3 : public T, public QObject { };
* class D : public B1, public B2, public B3, public T { Q_OBJECT };
* </pre>
* The list of bases for D will contain B1 and B2, but not B3 or T.
* <p>
* The list will be ordered as in the C++ code and will include only the directly declared
* base classes.
*/
public List<IQObject> getBases();
}

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2013 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.qt.core.index;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.cdt.qt.internal.core.index.QtFactory;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
/**
* The public interface to the Qt index. The Qt index is a small wrapper around the
* core CDT's CIndex that adds Qt specific information. The Qt index is designed to
* interpret multiple versions of Qt, however only 4.8 has been implemented for now.
*
* @see #getIndex(IProject)
*/
public abstract class QtIndex {
/**
* Return an instance of the Qt index for the argument project. The CDT index is
* examined to discover appropriate version of Qt (using the value of QT_VERSION).
* Returns null if the Qt index cannot be created. This could happen if the argument
* project does not have the qtnature, or if the value of QT_VERSION is not supported
* by this implementation.
*
* @param project A Qt enabled project that should be indexed with Qt-specific information.
* @return The Qt index or null if the index cannot be created.
*/
public static QtIndex getIndex(IProject project) {
if (project == null)
return null;
try {
Object index = project.getSessionProperty(QtPlugin.QTINDEX_PROP_NAME);
if (index instanceof QtIndex)
return (QtIndex)index;
} catch(CoreException e) {
QtPlugin.log(e);
}
// create and store a new instance when needed
QtIndex index = QtFactory.create(project);
if (index == null)
return null;
try {
project.setSessionProperty(QtPlugin.QTINDEX_PROP_NAME, index);
} catch( CoreException e ) {
QtPlugin.log(e);
}
return index;
}
/**
* Find and return a subclass of QObject with the given qualified name. Returns null if
* the index does not have a subclass of QObject with the given name.
*/
public abstract IQObject findQObject(String[] qualifiedName);
}

View file

@ -1,230 +0,0 @@
/*
* Copyright (c) 2013 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.qt.internal.core;
import org.eclipse.cdt.core.dom.ast.IASTDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTMacroExpansionLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion;
import org.eclipse.cdt.core.dom.ast.IASTProblemHolder;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTVisibilityLabel;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod;
import org.eclipse.cdt.core.dom.ast.tag.IBindingTagger;
import org.eclipse.cdt.core.dom.ast.tag.ITag;
import org.eclipse.cdt.core.dom.ast.tag.ITagWriter;
import org.eclipse.cdt.core.dom.ast.tag.IWritableTag;
import org.eclipse.cdt.qt.core.QtKeywords;
import org.eclipse.cdt.qt.core.QtPlugin;
/**
* Finds all functions that are marked as Qt signals or slots and tags them in
* the index. There are two ways that Qt understands for marking a function as a
* signal or slot: 1) With a macro in the function's visibility label 2) With a
* macro before the function itself E.g., both of these cases are valid:
*
* <pre>
* class T
* {
* private:
* Q_SLOT void some_slot();
*
* signals:
* void some_signal();
* };
* </pre>
*
* The 6 applicable macros are signals, Q_SIGNALS, Q_SIGNAL, slots, Q_SLOTS, and
* Q_SLOT.
*/
public class QtSignalSlotTagger implements IBindingTagger {
private static ICPPASTVisibilityLabel findVisibilityLabel(
ICPPMethod method, IASTNode ast) {
// the visibility cannot be found without an ast
if (ast == null)
return null;
IASTNode methodDecl = ast;
ICPPASTCompositeTypeSpecifier classType = null;
while (methodDecl != null && classType == null) {
IASTNode parent = methodDecl.getParent();
if (parent instanceof ICPPASTCompositeTypeSpecifier)
classType = (ICPPASTCompositeTypeSpecifier) parent;
else
methodDecl = parent;
}
if (methodDecl == null || classType == null)
return null;
ICPPASTVisibilityLabel lastLabel = null;
for (IASTDeclaration decl : classType.getMembers()) {
if (decl instanceof ICPPASTVisibilityLabel)
lastLabel = (ICPPASTVisibilityLabel) decl;
else if (decl == methodDecl)
return lastLabel;
}
return null;
}
private static byte getBitset(IASTNodeLocation... locations) {
for (IASTNodeLocation location : locations)
if (location instanceof IASTMacroExpansionLocation) {
IASTMacroExpansionLocation macroExpansion = (IASTMacroExpansionLocation) location;
IASTPreprocessorMacroExpansion exp = macroExpansion
.getExpansion();
String macro = exp.getMacroReference().toString();
if (QtKeywords.Q_SIGNAL.equals(macro)
|| QtKeywords.Q_SIGNALS.equals(macro)
|| QtKeywords.SIGNALS.equals(macro))
return QtPlugin.SignalSlot_Mask_signal;
if (QtKeywords.Q_SLOT.equals(macro)
|| QtKeywords.Q_SLOTS.equals(macro)
|| QtKeywords.SLOTS.equals(macro))
return QtPlugin.SignalSlot_Mask_slot;
}
return 0;
}
private static byte getBitset(IASTNode... nodes) {
byte bitset = 0;
for (IASTNode node : nodes)
if (node != null)
for (IASTNodeLocation loc : node.getNodeLocations())
bitset |= getBitset(loc);
return bitset;
}
private static IASTNode getSimpleDecl(IASTNode node) {
while (node != null && !(node instanceof IASTSimpleDeclaration))
node = node.getParent();
return node;
}
private byte getQtMarkers(ICPPMethod method, IASTName ast) {
byte bitset = 0;
if (ast == null)
return bitset;
// Look for macros on the previous visibility label.
bitset |= getBitset(findVisibilityLabel(method, ast));
// Look for macros on this function. See Bug 401696 for a better
// description of why it needs
// to work this why. Briefly, the parser does not associate empty macros
// with the function when
// they are the first thing in the declaration. E.g.,
// #define X
// void func1() {}
// X void func2() {}
// Could also look like:
// void func1() {} X
// void func2() {}
//
// The following code instead looks at the parents and children to find
// all node locations between
// the declarators.
//
// We first look at parents to find the closest SimpleDeclaration. We
// then look at that node's parent
// to find the node that is right before the target. Then we look at all
// node locations between the
// end of that previous node and the end of the target node.
// find the closest containing SimpleDecl
IASTNode simpleDecl = getSimpleDecl(ast);
IASTNode parent = simpleDecl == null ? null : simpleDecl.getParent();
if (parent == null)
return bitset;
// find the declaration before the target
IASTNode previous = null;
IASTNode[] children = parent.getChildren();
if (children.length > 1)
for (int i = 1; i < children.length; ++i) {
if (children[i] == simpleDecl) {
// if we haven't found a SimpleDecl, then find the nearest
// previous non-problem node
for (int j = i - 1; previous == null && j >= 0; --j)
if (!(children[j] instanceof IASTProblemHolder))
previous = children[j];
break;
}
if (children[i] instanceof IASTSimpleDeclaration)
previous = children[i];
}
// Signals/slots can only be declared inside of classes, so all cases we
// care about have a
// previous child, even if it is only the Base-class specifier.
if (previous == null)
return bitset;
IASTFileLocation prevLocation = previous.getFileLocation();
int prev_off = prevLocation.getNodeOffset();
int prev_end = prevLocation.getNodeOffset()
+ prevLocation.getNodeLength();
// Figure out where the target node ends.
int end = ast.getFileLocation().getNodeOffset()
+ ast.getFileLocation().getNodeLength();
// Examine all locations that appear after the previous node and before
// the target node.
boolean found_previous = false;
for (IASTNodeLocation loc : parent.getNodeLocations()) {
int o = loc.getNodeOffset();
int e = loc.getNodeOffset() + loc.getNodeLength();
// if the previous node has already been found, process this one
if (found_previous)
bitset |= getBitset(loc);
// otherwise see if this is the previous node
else if (o <= prev_off && e >= prev_end)
found_previous = true;
// stop processing when we're processed to the end of the target
if (e >= end)
break;
}
return bitset;
}
@Override
public ITag process(ITagWriter tagWriter, IBinding binding, IASTName ast) {
// only methods a be signals or slots
if (!(binding instanceof ICPPMethod))
return null;
// Find all qt marker macros associated with this node.
ICPPMethod method = (ICPPMethod) binding;
byte bitset = getQtMarkers(method, ast);
// create and store the bitset if needed
if (bitset != 0) {
IWritableTag tag = tagWriter.createTag(
QtPlugin.SIGNAL_SLOT_TAGGER_ID, 1);
if (tag != null && tag.putByte(0, bitset))
return tag;
}
return null;
}
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2013 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.qt.internal.core.index;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.core.runtime.CoreException;
/**
* A wrapper around the CDT index that manages the read lock.
*/
public class CDTIndex {
private final IIndex index;
public CDTIndex(IIndex index) {
this.index = index;
}
/**
* An object used for reading from the CDT index. The {@link #access(IIndex)} method
* will only be invoked when the index's read lock has been properly acquired.
*/
public static interface Accessor<T> {
/**
* A method that performs the lookup within the CDT index. The read-lock will
* be acquired before invoking this method.
* <p>
* <strong>The implementation of access must not make calls to {@link CDTIndex#get(Accessor)}.
* If other objects are needed, then have the accessor return a qualified name and
* lookup the object after the implementation of #access completes.</strong>
*/
public T access(IIndex index) throws CoreException;
}
/**
* Use the given accessor to find and return a value from the index. This method ensures
* that the read-lock has been acquired.
*/
public <T> T get(Accessor<T> accessor) {
try {
index.acquireReadLock();
} catch(InterruptedException e) {
return null;
}
try {
return accessor.access(index);
} catch(CoreException e) {
QtPlugin.log( e );
} finally {
index.releaseReadLock();
}
return null;
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2013 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.qt.internal.core.index;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQObject;
import org.eclipse.core.runtime.CoreException;
public class QObject implements IQObject {
private final String name;
private final QtPDOMQObject pdomQObject;
private final List<IQObject> bases;
public QObject(QtIndexImpl qtIndex, CDTIndex cdtIndex, QtPDOMQObject pdomQObject) throws CoreException {
this.name = pdomQObject.getName();
this.pdomQObject = pdomQObject;
this.bases = new ArrayList<IQObject>();
for(QtPDOMQObject base : pdomQObject.findBases())
this.bases.add(new QObject(qtIndex, cdtIndex, base));
}
@Override
public IBinding getBinding() {
return pdomQObject;
}
@Override
public String getName() {
return name;
}
@Override
public List<IQObject> getBases() {
return bases;
}
}

View file

@ -0,0 +1,122 @@
/*
* Copyright (c) 2013 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.qt.internal.core.index;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexMacro;
import org.eclipse.cdt.core.index.IndexFilter;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.cdt.qt.core.index.QtIndex;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
public class QtFactory {
private static final char[] QT_VERSION = "QT_VERSION".toCharArray();
public static QtIndex create(IProject project) {
CDTIndex cdtIndex = getCDTIndex(project);
if (cdtIndex == null) {
QtPlugin.log("could not get CDT index from project " + project.getName());
return null;
}
QtVersion qtVersion = cdtIndex.get(QtVersionAccessor);
if (qtVersion == null) {
QtPlugin.log("could not find Qt version in CDT index from project " + project.getName());
return null;
}
if (qtVersion.major == 4 && qtVersion.minor == 8)
return new QtIndexImpl(cdtIndex);
// Qt 4.8 is the default implementation, 5.0 support will need to come soon
return new QtIndexImpl(cdtIndex);
}
private static CDTIndex getCDTIndex(IProject project) {
if (project == null)
return null;
ICProject cProject = CoreModel.getDefault().create(project);
if (cProject == null)
return null;
IIndex index = null;
try {
index = CCorePlugin.getIndexManager().getIndex(cProject);
} catch( CoreException e ) {
QtPlugin.log(e);
return null;
}
return index == null ? null : new CDTIndex(index);
}
/**
* A small wrapper to hold the result of index lookups for the Qt version.
*/
private static class QtVersion {
public final int major;
public final int minor;
@SuppressWarnings("unused")
public final int patch;
// QT_VERSION looks like 0x040805
private static final Pattern Version_regex = Pattern.compile( "0x([a-fA-F\\d]{1,2})([a-fA-F\\d]{2})([a-fA-F\\d]{2})" );
public static QtVersion create(String version) {
Matcher m = Version_regex.matcher(version);
if (!m.matches())
return null;
try {
int major = Integer.parseInt(m.group(1), 16);
int minor = Integer.parseInt(m.group(2), 16);
int patch = Integer.parseInt(m.group(3), 16);
return new QtVersion(major, minor, patch);
} catch(NumberFormatException e) {
QtPlugin.log(e);
}
return null;
}
private QtVersion(int major, int minor, int patch) {
this.major = major;
this.minor = minor;
this.patch = patch;
}
}
private static final CDTIndex.Accessor<QtVersion> QtVersionAccessor = new CDTIndex.Accessor<QtVersion>() {
@Override
public QtVersion access(IIndex index) throws CoreException {
// Multiple macros might be found, sort the values and choose the highest version.
SortedSet<String> versions = new TreeSet<String>();
try {
for(IIndexMacro macro : index.findMacros(QT_VERSION, IndexFilter.ALL, null))
versions.add(new String(macro.getExpansion()).toLowerCase());
} catch( CoreException e ) { }
// don't create the Qt index if there is no Qt information in the CDT index
if (versions.size() <= 0)
return null;
// the highest version has been sorted to the last position
return QtVersion.create(versions.last());
}
};
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2013 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.qt.internal.core.index;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IndexFilter;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.QtIndex;
import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQObject;
import org.eclipse.core.runtime.CoreException;
public class QtIndexImpl extends QtIndex {
private final CDTIndex cdtIndex;
private static final IndexFilter QtLinkageFilter = new IndexFilter() {
@Override
public boolean acceptLinkage(ILinkage linkage) {
return linkage.getLinkageID() == ILinkage.QT_LINKAGE_ID;
}
@Override
public boolean acceptBinding(IBinding binding) throws CoreException {
return true;
}
};
public QtIndexImpl(CDTIndex cdtIndex) {
this.cdtIndex = cdtIndex;
}
@Override
public IQObject findQObject(String[] name) {
return name == null ? null : cdtIndex.get(new QObjectImplAccessor(name));
}
private class QObjectImplAccessor implements CDTIndex.Accessor<IQObject> {
private final char[][] name;
public QObjectImplAccessor(String[] qualName) {
name = new char[qualName.length][];
for(int i = 0; i < name.length; ++i)
name[i] = qualName[i].toCharArray();
}
@Override
public IQObject access(IIndex index) throws CoreException {
// TODO can there be more than one result?
for(IIndexBinding binding : index.findBindings(name, QtLinkageFilter, null))
if (binding instanceof QtPDOMQObject)
return new QObject(QtIndexImpl.this, cdtIndex, (QtPDOMQObject) binding);
return null;
}
}
}

View file

@ -0,0 +1,280 @@
/*
* Copyright (c) 2013 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.qt.internal.core.pdom;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.ExpansionOverlapsBoundaryException;
import org.eclipse.cdt.core.dom.ast.IASTCompletionContext;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTImageLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNameOwner;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.parser.IToken;
/**
* The Qt linkage introduces several names that are based on names from the C++ linkage. This
* utility class is used to delegate operations to that base C++ name. Methods can be overridden
* by implementing in a subclass.
*
* @see QObjectName
*/
public abstract class ASTDelegatedName implements IASTName {
protected final IASTName delegate;
protected IBinding binding;
/**
* Some Qt elements are introduced with empty macro expansions. The Qt linkage handles this
* by creating a new name and then adding it as a reference to the C++ language element.
* This utility helps by containing that C++ name and the location of the Qt name.
*/
public static class Reference extends ASTDelegatedName {
private final IASTFileLocation location;
public Reference(IASTName name, IASTFileLocation location) {
super(name);
this.location = location;
}
@Override
protected IBinding createBinding() {
return delegate.resolveBinding();
}
@Override
public IASTFileLocation getFileLocation() {
return location;
}
@Override
public boolean isReference() {
return true;
}
@Override
public boolean isDefinition() {
return false;
}
@Override
public boolean isDeclaration() {
return false;
}
@Override
public int getRoleOfName(boolean allowResolution) {
return IASTNameOwner.r_reference;
}
}
protected abstract IBinding createBinding();
protected ASTDelegatedName(IASTName delegate) {
this.delegate = delegate;
}
@Override
public IASTTranslationUnit getTranslationUnit() {
return delegate.getTranslationUnit();
}
@Override
public IASTNodeLocation[] getNodeLocations() {
return delegate.getNodeLocations();
}
@Override
public IASTFileLocation getFileLocation() {
return delegate.getFileLocation();
}
@Override
public String getContainingFilename() {
return delegate.getContainingFilename();
}
@Override
public boolean isPartOfTranslationUnitFile() {
return delegate.isPartOfTranslationUnitFile();
}
@Override
public IASTNode getParent() {
return delegate.getParent();
}
@Override
public IASTNode[] getChildren() {
return delegate.getChildren();
}
@Override
public void setParent(IASTNode node) {
delegate.setParent(node);
}
@Override
public ASTNodeProperty getPropertyInParent() {
return delegate.getPropertyInParent();
}
@Override
public void setPropertyInParent(ASTNodeProperty property) {
delegate.setPropertyInParent(property);
}
@Override
public boolean accept(ASTVisitor visitor) {
return delegate.accept(visitor);
}
@Override
public String getRawSignature() {
return delegate.getRawSignature();
}
@Override
public boolean contains(IASTNode node) {
return delegate.contains(node);
}
@Override
public IToken getLeadingSyntax() throws ExpansionOverlapsBoundaryException, UnsupportedOperationException {
return delegate.getLeadingSyntax();
}
@Override
public IToken getTrailingSyntax() throws ExpansionOverlapsBoundaryException, UnsupportedOperationException {
return delegate.getTrailingSyntax();
}
@Override
public IToken getSyntax() throws ExpansionOverlapsBoundaryException {
return delegate.getSyntax();
}
@Override
public boolean isFrozen() {
return delegate.isFrozen();
}
@Override
public boolean isActive() {
return delegate.isActive();
}
@Override
public IASTNode getOriginalNode() {
return delegate.getOriginalNode();
}
@Override
public char[] getSimpleID() {
return delegate.getSimpleID();
}
@Override
public boolean isDeclaration() {
return delegate.isDeclaration();
}
@Override
public boolean isReference() {
return delegate.isReference();
}
@Override
public boolean isDefinition() {
return delegate.isDefinition();
}
@Override
public char[] toCharArray() {
return delegate.toCharArray();
}
@Override
public IBinding getBinding() {
return binding;
}
@Override
public IBinding resolveBinding() {
if (binding == null)
binding = createBinding();
return binding;
}
@Override
public int getRoleOfName(boolean allowResolution) {
return delegate.getRoleOfName(allowResolution);
}
@Override
public IASTCompletionContext getCompletionContext() {
return delegate.getCompletionContext();
}
@Override
public ILinkage getLinkage() {
return delegate.getLinkage();
}
@Override
public IASTImageLocation getImageLocation() {
return delegate.getImageLocation();
}
@Override
public IASTName getLastName() {
return delegate.getLastName();
}
@Override
public IASTName copy() {
return delegate.copy();
}
@Override
public IASTName copy(CopyStyle style) {
return delegate.copy(style);
}
@Override
public void setBinding(IBinding binding) {
this.binding = binding;
}
@Override
public char[] getLookupKey() {
return delegate.getLookupKey();
}
@Override
public IBinding getPreBinding() {
return binding;
}
@Override
public IBinding resolvePreBinding() {
return resolveBinding();
}
@Override
public boolean isQualified() {
return delegate.isQualified();
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2013 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.qt.internal.core.pdom;
import org.eclipse.cdt.internal.core.pdom.PDOM;
import org.eclipse.cdt.internal.core.pdom.dom.IPDOMLinkageFactory;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
public class PDOMQtLinkageFactory implements IPDOMLinkageFactory {
@Override
public PDOMLinkage getLinkage(PDOM pdom, long record) {
try {
return new QtPDOMLinkage(pdom, record);
} catch(CoreException e) {
QtPlugin.log(e);
}
return null;
}
@Override
public PDOMLinkage createLinkage(PDOM pdom) throws CoreException {
return new QtPDOMLinkage(pdom);
}
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2013 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.qt.internal.core.pdom;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.internal.core.dom.Linkage;
/**
* QObjects are C++ classes that have been annotated with Qt marker macros. This class is
* used to introduce the QObject to the Qt linkage.
*/
@SuppressWarnings("restriction")
public class QObjectName extends ASTDelegatedName {
private final ICPPASTCompositeTypeSpecifier spec;
private IASTNode parent;
private ASTNodeProperty propertyInParent;
public QObjectName(ICPPASTCompositeTypeSpecifier spec) {
super(spec.getName());
this.spec = spec;
this.parent = delegate.getParent();
this.propertyInParent = delegate.getPropertyInParent();
}
@Override
protected IBinding createBinding() {
return new QtBinding(QtPDOMNodeType.QObject, this, spec.getName());
}
@Override
public IASTTranslationUnit getTranslationUnit() {
return spec.getTranslationUnit();
}
@Override
public IASTNode[] getChildren() {
return IASTNode.EMPTY_NODE_ARRAY;
}
@Override
public IASTNode getParent() {
return parent;
}
@Override
public void setParent(IASTNode node) {
parent = node;
}
@Override
public ASTNodeProperty getPropertyInParent() {
return propertyInParent;
}
@Override
public void setPropertyInParent(ASTNodeProperty property) {
propertyInParent = property;
}
@Override
public boolean accept(ASTVisitor visitor) {
return false;
}
@Override
public boolean contains(IASTNode node) {
return false;
}
@Override
public ILinkage getLinkage() {
return Linkage.QT_LINKAGE;
}
@Override
public IASTName copy() {
return copy(CopyStyle.withoutLocations);
}
@Override
public IASTName copy(CopyStyle style) {
return new QObjectName(spec);
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2013 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.qt.internal.core.pdom;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.index.IIndexSymbols;
import org.eclipse.cdt.internal.core.parser.scanner.LocationMap;
import org.eclipse.cdt.qt.core.QtKeywords;
@SuppressWarnings("restriction")
public class QtASTVisitor extends ASTVisitor {
private final IIndexSymbols symbols;
private final LocationMap locationMap;
public QtASTVisitor(IIndexSymbols symbols, LocationMap locationMap) {
shouldVisitDeclSpecifiers = true;
this.symbols = symbols;
this.locationMap = locationMap;
}
private boolean isQObject(ICPPASTCompositeTypeSpecifier spec, IASTPreprocessorMacroExpansion[] expansions) {
// The class definition must contain a Q_OBJECT expansion.
for (IASTPreprocessorMacroExpansion expansion : expansions) {
IASTPreprocessorMacroDefinition macro = expansion.getMacroDefinition();
if (QtKeywords.Q_OBJECT.equals(String.valueOf(macro.getName())))
return true;
}
return false;
}
private void handleQObject(IASTPreprocessorIncludeStatement owner, ICPPASTCompositeTypeSpecifier spec, IASTPreprocessorMacroExpansion[] expansions) {
// Put the QObject into the symbol map.
QObjectName qobjName = new QObjectName(spec);
symbols.add(owner, qobjName, null);
}
@Override
public int visit(IASTDeclSpecifier declSpec) {
if (declSpec instanceof ICPPASTCompositeTypeSpecifier) {
ICPPASTCompositeTypeSpecifier spec = (ICPPASTCompositeTypeSpecifier) declSpec;
IASTFileLocation loc = spec.getFileLocation();
IASTPreprocessorIncludeStatement owner = loc == null ? null : loc.getContextInclusionStatement();
IASTPreprocessorMacroExpansion[] expansions = locationMap.getMacroExpansions(loc);
if (isQObject(spec, expansions))
handleQObject(owner, spec, expansions);
}
return super.visit(declSpec);
}
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2013 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.qt.internal.core.pdom;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.DOMException;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.internal.core.dom.Linkage;
@SuppressWarnings("restriction")
public class QtBinding implements IBinding {
private final QtPDOMNodeType type;
private final QtBinding owner;
private final IASTName qtName;
private final IASTName cppName;
private QtPDOMBinding pdomBinding;
public QtBinding(QtPDOMNodeType type, IASTName qtName, IASTName cppName) {
this(type, null, qtName, cppName);
}
public QtBinding(QtPDOMNodeType type, QtBinding owner, IASTName qtName, IASTName cppName) {
this.type = type;
this.owner = owner;
this.qtName = qtName;
this.cppName = cppName;
}
public QtPDOMNodeType getType() {
return type;
}
public IASTName getQtName() {
return qtName;
}
public IASTName getCppName() {
return cppName;
}
public void setPDOMBinding(QtPDOMBinding pdomBinding) {
this.pdomBinding = pdomBinding;
}
@Override
@SuppressWarnings({ "rawtypes" })
public Object getAdapter(Class adapter) {
if (getClass().isAssignableFrom(adapter))
return this;
if (QtPDOMBinding.class.isAssignableFrom(adapter))
return pdomBinding;
return null;
}
@Override
public String getName() {
return String.valueOf(getNameCharArray());
}
@Override
public char[] getNameCharArray() {
return qtName.getSimpleID();
}
@Override
public ILinkage getLinkage() {
return Linkage.QT_LINKAGE;
}
@Override
public IBinding getOwner() {
return owner;
}
@Override
public IScope getScope() throws DOMException {
return null;
}
}

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2013 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.qt.internal.core.pdom;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.index.IIndexSymbols;
import org.eclipse.cdt.core.index.IPDOMASTProcessor;
import org.eclipse.cdt.internal.core.parser.scanner.LocationMap;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
public class QtPDOMASTProcessor extends IPDOMASTProcessor.Abstract {
@Override
public int process(IASTTranslationUnit ast, IIndexSymbols symbols) throws CoreException {
ast.accept(new QtASTVisitor(symbols, (LocationMap) ast.getAdapter(LocationMap.class)));
return ILinkage.QT_LINKAGE_ID;
}
}

View file

@ -0,0 +1,98 @@
/*
* Copyright (c) 2013 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.qt.internal.core.pdom;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
public abstract class QtPDOMBinding extends PDOMBinding {
private static int offsetInitializer = RECORD_SIZE;
protected static enum Field {
CppRecord(Database.PTR_SIZE),
Last(0);
public final int offset;
private Field(int sizeof) {
this.offset = offsetInitializer;
offsetInitializer += sizeof;
}
public long getRecord(long baseRec) {
return baseRec + offset;
}
}
protected QtPDOMBinding(QtPDOMLinkage linkage, long record) {
super(linkage, record);
}
protected QtPDOMBinding(QtPDOMLinkage linkage, PDOMNode parent, QtBinding qtBinding) throws CoreException {
super(linkage, parent, qtBinding.getNameCharArray());
qtBinding.setPDOMBinding(this);
getDB().putRecPtr(Field.CppRecord.getRecord(record), linkage.getCPPRecord(qtBinding));
}
@Override
protected int getRecordSize() {
return Field.Last.offset;
}
public long getCppRecord() {
try {
return getDB().getRecPtr(Field.CppRecord.getRecord(record));
} catch (CoreException e) {
QtPlugin.log(e);
}
return 0;
}
public IBinding getCppBinding() throws CoreException {
long cppRec = getCppRecord();
if (cppRec == 0)
return null;
PDOMLinkage cppLinkage = getPDOM().getLinkage(ILinkage.CPP_LINKAGE_ID);
if (cppLinkage == null)
return null;
return cppLinkage.getBinding(cppRec);
}
protected QtPDOMLinkage getQtLinkage() {
PDOMLinkage pdomLinkage = getLinkage();
return pdomLinkage instanceof QtPDOMLinkage ? (QtPDOMLinkage) pdomLinkage : null;
}
// Access to the base class is restricted in the cdt.core plugin. Other classes in the qt.core
// plugin that need the qualified name get an access warning. This forwarding function moves
// those warnings to a single place (this method).
@Override
public String[] getQualifiedName() {
return super.getQualifiedName();
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object getAdapter(Class adapter) {
if (adapter.isAssignableFrom(getClass()))
return this;
return super.getAdapter(adapter);
}
}

View file

@ -0,0 +1,245 @@
/*
* Copyright (c) 2013 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.qt.internal.core.pdom;
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.ISerializableEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.ITypeMarshalBuffer;
import org.eclipse.cdt.internal.core.pdom.PDOM;
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.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.PDOMLinkage;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode;
import org.eclipse.cdt.qt.core.QtPlugin;
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),
CppIndex(Database.PTR_SIZE),
Last(0);
private final int offset;
private Field(int sizeof) {
this.offset = offsetInitializer;
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;
// An index of C++ -> Qt Bindings. This is used for fast lookup of things like the QObject
// for a C++ class in the base specifier list. Each entry in the index is a pair like
// { CPP_Record, Qt_Record }, the CPP_Record is used for comparison when searching the index.
private final BTree cppIndex;
public QtPDOMLinkage(PDOM pdom, long record) throws CoreException {
super(pdom, record);
version = pdom.getDB().getInt(Field.Version.getRecord(record));
cppIndex = new BTree(pdom.getDB(), Field.CppIndex.getRecord(record), new CppRecordIndexComparator());
}
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);
long cppIndexRec = Field.CppIndex.getRecord(record);
Database db = pdom.getDB();
db.putRecPtr(cppIndexRec, 0);
cppIndex = new BTree(db, cppIndexRec, new CppRecordIndexComparator());
}
public int getVersion() {
return version;
}
@Override
public String getLinkageName() {
return ILinkage.QT_LINKAGE_NAME;
}
@Override
public int getLinkageID() {
return ILinkage.QT_LINKAGE_ID;
}
public QtPDOMBinding findFromCppRecord(long cppRec) throws CoreException {
CppRecordIndexFinder finder = new CppRecordIndexFinder(cppRec);
cppIndex.accept(finder);
if (finder.foundQtRec == null)
return null;
PDOMNode node = getNode(finder.foundQtRec.longValue());
return node instanceof QtPDOMBinding ? (QtPDOMBinding) node : null;
}
@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);
}
// 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 create. 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;
// Otherwise try to create a new PDOMBinding.
QtBinding qtBinding = (QtBinding) binding.getAdapter(QtBinding.class);
if (qtBinding != null)
switch(qtBinding.getType()) {
case QObject:
pdomBinding = new QtPDOMQObject(this, qtBinding);
break;
}
// 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 void addChild(QtPDOMBinding child) throws CoreException {
super.addChild(child);
Database db = getDB();
long pair = db.malloc(Database.PTR_SIZE * 2);
db.putRecPtr(pair, child.getCppRecord());
db.putRecPtr(pair + Database.PTR_SIZE, child.getRecord());
cppIndex.insert(pair);
}
public long getCPPRecord(QtBinding qtBinding) throws CoreException {
IASTName cppName = qtBinding.getCppName();
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();
}
@Override
public PDOMBinding addBinding(IASTName name) throws CoreException {
return name == null ? null : adaptBinding(name.getBinding());
}
@Override
public int getBindingType(IBinding binding) {
return binding instanceof QtBinding ? ((QtBinding) binding).getType().Type : 0;
}
@Override
public PDOMBinding addTypeBinding(IBinding binding) throws CoreException {
throw new CoreException(QtPlugin.error("Qt Linkage does not manage types")); //$NON-NLS-1$
}
@Override
public IType unmarshalType(ITypeMarshalBuffer buffer) throws CoreException {
throw new CoreException(QtPlugin.error("Qt Linkage does not marshal types")); //$NON-NLS-1$
}
@Override
public IBinding unmarshalBinding(ITypeMarshalBuffer buffer) throws CoreException {
throw new CoreException(QtPlugin.error("Qt Linkage does not marshal bindings")); //$NON-NLS-1$
}
@Override
public ISerializableEvaluation unmarshalEvaluation(ITypeMarshalBuffer typeMarshalBuffer) throws CoreException {
throw new CoreException(QtPlugin.error("Qt Linkage does not marshal evaluations")); //$NON-NLS-1$
}
private class CppRecordIndexComparator implements IBTreeComparator {
@Override
public int compare(long record1, long record2) throws CoreException {
Database db = getDB();
Long cppRec1 = Long.valueOf(db.getRecPtr(record1));
long cppRec2 = Long.valueOf(db.getRecPtr(record2));
return cppRec1.compareTo(cppRec2);
}
}
private class CppRecordIndexFinder extends CppRecordIndexComparator implements IBTreeVisitor {
private final Long targetCppRec;
public Long foundQtRec;
public CppRecordIndexFinder(long targetCppRec) {
this.targetCppRec = Long.valueOf(targetCppRec);
}
@Override
public int compare(long record) throws CoreException {
Long cppRec = Long.valueOf(getDB().getRecPtr(record));
return cppRec.compareTo(targetCppRec);
}
@Override
public boolean visit(long record) throws CoreException {
// Stop searching after the record is found.
if (foundQtRec != null)
return false;
// The record is the pair, so the Qt rec is the second element.
long qtRec = getDB().getRecPtr(record + Database.PTR_SIZE);
foundQtRec = Long.valueOf(qtRec);
return false;
}
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2013 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.qt.internal.core.pdom;
import org.eclipse.cdt.internal.core.index.IIndexBindingConstants;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
public enum QtPDOMNodeType {
QObject;
public final int Type = IIndexBindingConstants.LAST_CONSTANT + 1 + ordinal();
/**
* The current version of the QtPDOMLinkage. This can be used to make sure the persisted
* data matches what is expected by the implementation. Care should be taken to make changes
* backward compatible when possible.
* <p>
* The version is needed because ordinals for these enumerators are written to the file.
*/
public static final int VERSION = 1;
public static QtPDOMNodeType forType(int version, int type) {
// Nothing has been deleted or replaced yet, so the version is ignored.
for(QtPDOMNodeType node : values())
if (node.Type == type)
return node;
return null;
}
// This needs to return PDOMNode so that it can be either QtPDOMNode or QtPDOMBinding.
public static PDOMNode load(QtPDOMLinkage linkage, int nodeType, long record) throws CoreException {
QtPDOMNodeType node = QtPDOMNodeType.forType(linkage.getVersion(), nodeType);
if (node == null)
return null;
switch(node) {
case QObject:
return new QtPDOMQObject(linkage, record);
}
return null;
}
}

View file

@ -0,0 +1,182 @@
/*
* Copyright (c) 2013 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.qt.internal.core.pdom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType;
import org.eclipse.cdt.internal.core.pdom.db.PDOMNodeLinkedList;
import org.eclipse.cdt.internal.core.pdom.dom.IPDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.core.runtime.CoreException;
/**
* The persisted form of QObjects.
*/
@SuppressWarnings("restriction")
public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
// The RecordSize is initialized with the size of the parent. It is incremented during
// loading of the Fields enum. This value does not reliably store the size of the
// QtPDOMQObject record because the enum will not be initialized until it is needed.
// The record size is retrieved as the offset of the special terminal enumerator Last.
private static int offsetInitializer = QtPDOMBinding.Field.Last.offset;
protected static enum Field {
Children(4 /* From PDOMNodeLinkedList.RECORD_SIZE, which is protected */),
Last(0);
private final int offset;
private Field(int sizeof) {
this.offset = offsetInitializer;
offsetInitializer += sizeof;
}
public long getRecord(long baseRec) {
return baseRec + offset;
}
}
private final PDOMNodeLinkedList children;
protected QtPDOMQObject(QtPDOMLinkage linkage, long record) throws CoreException {
super(linkage, record);
children = new PDOMNodeLinkedList(linkage, Field.Children.getRecord(record));
}
public QtPDOMQObject(QtPDOMLinkage linkage, QtBinding binding) throws CoreException {
super(linkage, null, binding);
children = new PDOMNodeLinkedList(linkage, Field.Children.getRecord(record));
}
@Override
protected int getRecordSize() {
return Field.Last.offset;
}
@Override
public int getNodeType() {
return QtPDOMNodeType.QObject.Type;
}
// This forwarding method is to get rid of compilation warnings when clients try to call
// #getName on the non-accessible parent.
@Override
public String getName() {
return super.getName();
}
public List<QtPDOMQObject> findBases() throws CoreException {
IBinding cppBinding = getCppBinding();
if (!(cppBinding instanceof ICPPClassType))
return Collections.emptyList();
List<QtPDOMQObject> bases = new ArrayList<QtPDOMQObject>();
for (ICPPBase base : ((ICPPClassType) cppBinding).getBases()) {
if (base.getVisibility() != ICPPBase.v_public)
continue;
IBinding baseCls = base.getBaseClass();
if (baseCls == null)
continue;
IPDOMBinding pdomBase = (IPDOMBinding) baseCls.getAdapter(IPDOMBinding.class);
if (pdomBase == null)
continue;
QtPDOMBinding qtPDOMBinding = getQtLinkage().findFromCppRecord(pdomBase.getRecord());
if (qtPDOMBinding == null)
continue;
QtPDOMQObject pdomQObj = (QtPDOMQObject) qtPDOMBinding.getAdapter(QtPDOMQObject.class);
if (pdomQObj != null)
bases.add(pdomQObj);
}
return bases;
}
@Override
public boolean isSameType(IType type) {
if (type == this)
return true;
if (!(type instanceof QtPDOMQObject))
return false;
QtPDOMQObject other = (QtPDOMQObject) type;
return getRecord() == other.getRecord()
&& getLinkage().equals(other.getLinkage());
}
@Override
public int getKey() {
return ICPPClassType.k_class;
}
@Override
public boolean isAnonymous() {
return false;
}
@Override
public void addChild(PDOMNode child) throws CoreException {
children.addMember(child);
}
@Override
public IField[] getFields() {
QtPDOMVisitor.All<IField> collector = new QtPDOMVisitor.All<IField>(IField.class);
try {
children.accept(collector);
} catch(CoreException e) {
QtPlugin.log(e);
return IField.EMPTY_FIELD_ARRAY;
}
return collector.list.toArray(new IField[collector.list.size()]);
}
@Override
public IField findField(String name) {
QtPDOMVisitor.IFilter filter = new QtPDOMVisitor.PDOMNamedNodeFilter(name);
QtPDOMVisitor.Find<IField> finder = new QtPDOMVisitor.Find<IField>(IField.class, filter);
try {
accept(finder);
} catch(CoreException e) {
QtPlugin.log(e);
}
return finder.element;
}
@Override
public IScope getCompositeScope() {
try {
IBinding cppBinding = getCppBinding();
if (cppBinding instanceof ICompositeType)
return ((ICompositeType) cppBinding).getCompositeScope();
} catch(CoreException e) {
QtPlugin.log(e);
}
return null;
}
@Override
public Object clone() {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2013 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.qt.internal.core.pdom;
import java.util.ArrayList;
import java.util.Arrays;
import org.eclipse.cdt.core.dom.IPDOMNode;
import org.eclipse.cdt.core.dom.IPDOMVisitor;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNamedNode;
import org.eclipse.core.runtime.CoreException;
/**
* A Qt-specific specialization of the generic PDOMVisitor. This class provides
* an empty implementation of {@link #leave(IPDOMNode)}, but required implementations to
* provide {@link #visit(IPDOMNode)}. The class also provides a few commonly required
* implementations.
*/
@SuppressWarnings("restriction")
public abstract class QtPDOMVisitor implements IPDOMVisitor {
/**
* Collects all nodes that match the given type. This could be used, for example, to get
* all QtPDOMQObject's from the index.
*/
public static class All<T> extends QtPDOMVisitor {
private final Class<T> cls;
public final ArrayList<T> list = new ArrayList<T>();
public All(Class<T> cls) {
this.cls = cls;
}
@Override
@SuppressWarnings("unchecked")
public boolean visit(IPDOMNode node) throws CoreException {
if (node != null
&& cls.isAssignableFrom(node.getClass()))
list.add((T) node);
return true;
}
}
/**
* A simple interface that is used to select node's from the index based on specific
* criteria.
*/
public static interface IFilter {
public boolean matches(IPDOMNode node) throws CoreException;
}
/**
* A filter that selects nodes based on their name.
*/
public static class PDOMNamedNodeFilter implements IFilter {
private final char[] name;
public PDOMNamedNodeFilter(String name) {
this.name = name.toCharArray();
}
@Override
public boolean matches(IPDOMNode node) throws CoreException {
if (node instanceof PDOMNamedNode)
return Arrays.equals(name, ((PDOMNamedNode) node).getNameCharArray());
return false;
}
}
/**
* A utility class that searches the index for all nodes that match the given filter.
*/
public static class Find<T> extends QtPDOMVisitor {
private final Class<T> cls;
private final IFilter filter;
public T element;
public Find(Class<T> cls, IFilter filter) {
this.cls = cls;
this.filter = filter;
}
@Override
@SuppressWarnings("unchecked")
public boolean visit(IPDOMNode node) throws CoreException {
if (element != null)
return false;
if (cls.isAssignableFrom(node.getClass())
&& filter.matches(node))
element = (T) node;
return element == null;
}
}
@Override
public void leave(IPDOMNode node) throws CoreException {
}
}

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.eclipse.cdt.qt.tests</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View file

@ -0,0 +1,19 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tests
Bundle-SymbolicName: org.eclipse.cdt.qt.tests
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: org.eclipse.cdt.qt.tests.QtTestPlugin
Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
org.junit,
org.eclipse.cdt.qt.core,
org.eclipse.cdt.core.tests,
org.eclipse.cdt.core,
org.eclipse.core.resources,
org.eclipse.cdt.codan.core,
org.eclipse.cdt.qt.ui,
org.eclipse.jface.text,
org.eclipse.cdt.ui
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6

View file

@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2013 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.qt.tests;
import junit.framework.Test;
import junit.framework.TestSuite;
public class AllQtTests extends TestSuite {
public static Test suite() throws Exception {
return
new TestSuite(
QObjectTests.class,
QtIndexTests.class);
}
}

View file

@ -0,0 +1,167 @@
/*
* Copyright (c) 2013 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.qt.tests;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.dom.IPDOMManager;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexManager;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.testplugin.CProjectHelper;
import org.eclipse.cdt.core.testplugin.util.BaseTestCase;
import org.eclipse.cdt.core.testplugin.util.TestSourceReader;
import org.eclipse.cdt.qt.core.QtNature;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
public class BaseQtTestCase extends BaseTestCase {
// TODO There is a problem with the unit test framework where it sometimes will not wait
// long enough for the index to be updated. For now mask this problem by stopping
// that test and continuing with the rest.
@Deprecated
protected boolean isIndexOk(String indexName, Object obj) {
if (obj != null)
return true;
System.err.println(getClass().getSimpleName() + '.' + getName() + ": could not find " + indexName + " in the index, continuing with other tests");
return false;
}
protected IProject fProject;
protected IFile fFile;
protected ICProject fCProject;
protected IIndex fIndex;
@Override
protected void setUp() throws Exception {
super.setUp();
String projectName = "__" + getClass().getSimpleName() + "__";
fCProject = CProjectHelper.createCCProject(projectName, "bin", IPDOMManager.ID_FAST_INDEXER);
fProject = fCProject.getProject();
CProjectHelper.addNatureToProject(fProject, QtNature.ID, null);
fIndex = CCorePlugin.getIndexManager().getIndex(fCProject);
indexQObject_h();
}
@Override
protected void tearDown() throws Exception {
if (fCProject != null)
CProjectHelper.delete(fCProject);
fIndex = null;
fCProject = null;
fProject = null;
super.tearDown();
}
/**
* This creates a mock Qt header file, which avoids putting the real Qt headers into the
* include path of this unit test's fake project.
*/
// #define QT_VERSION 0x040805
// #define Q_PROPERTY(defn)
// #define Q_OBJECT
// #define Q_GADGET
// #define Q_CLASSINFO(k,v)
// #define Q_SIGNAL
// #define Q_SLOT
// #define Q_INVOKABLE
// #define Q_DECLARE_FLAGS(t,e)
// #define Q_ENUMS(e)
// #define Q_FLAGS(e)
// #define slots
// #define signals protected
// #define Q_SLOTS
// #define Q_SIGNALS protected
// const char *qFlagLocation(const char *method);
// #define SLOT(a) qFlagLocation("1"#a)
// #define SIGNAL(a) qFlagLocation("2"#a)
// #define QML_DECLARE_TYPEINFO( T, F ) template <> struct QDeclarativeTypeInfo<T> { enum { H = F }; };
// enum { QML_HAS_ATTACHED_PROPERTIES = 0x01 };
// class QObject
// {
// Q_OBJECT
// Q_SIGNAL void destroyed( QObject * );
// public:
// static bool connect( QObject *, const char *, QObject *, const char * );
// };
// class QString { public: QString( const char * ch ); };
// template<typename T> int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
// template<typename T, int metaObjectRevision> int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
// template<typename T> int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason);
public void indexQObject_h() throws Exception {
loadComment("junit-QObject.hh");
}
private String[] getContentsForTest(int blocks) throws Exception {
String callingMethod = Thread.currentThread().getStackTrace()[3].getMethodName();
CharSequence[] help= TestSourceReader.getContentsForTest(
QtTestPlugin.getDefault().getBundle(), "src", getClass(), callingMethod, blocks);
String[] result= new String[help.length];
int i= 0;
for (CharSequence buf : help) {
result[i++]= buf.toString();
}
return result;
}
/**
* The implementation of TestSourceReader (called from BaseTestCase) imposes some restrictions
* on the caller of #loadComment.
* <ol>
* <li>loadComment must be called from a public method</li>
* <li>loadComment must be called from a method that does not accept parameters</li>
* </ol>
*/
protected void loadComment(String filename) throws Exception {
String[] contents= getContentsForTest(1);
// get the timestamp of the last change to the index
IIndexManager indexManager = CCorePlugin.getIndexManager();
long timestamp = indexManager.getIndex(fCProject).getLastWriteAccess();
// add the new content
fFile = TestSourceReader.createFile(fProject, filename, contents[0]);
// wait for the index to change
Thread.yield();
for(long stopAt = System.currentTimeMillis() + 3000;
System.currentTimeMillis() < stopAt && timestamp == indexManager.getIndex(fCProject).getLastWriteAccess();
Thread.sleep(100)) {
/* intentionally empty*/
}
assertNotSame(timestamp, indexManager.getIndex(fCProject).getLastWriteAccess());
}
/**
* A utility method for pausing the JUNIT code. This is helpful when investigating the
* CDT indexer, which runs in a different job. The idea is to use it only while debugging,
* and to change the value of the pause variable in the loop in order to continue.
*/
protected static void pause() throws Exception {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName("*** JUNIT PAUSED ***");
try
{
// pause = false
boolean pause = true;
do
{
Thread.sleep(10000);
} while( pause );
} finally {
Thread.currentThread().setName(oldName);
}
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2013 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.qt.tests;
import java.util.Collection;
import java.util.Iterator;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.QtIndex;
public class QObjectTests extends BaseQtTestCase {
// #include "junit-QObject.hh"
// class T {};
// class B1 : public QObject {Q_OBJECT};
// class B2 : public QObject {Q_OBJECT};
// class B3 : public QObject {Q_OBJECT};
// class D1 : public B1, public B2, private B3, public T {Q_OBJECT};
// class D2 : public T, public QObject {};
public void testGetBases() throws Exception {
loadComment("bases.hh");
QtIndex qtIndex = QtIndex.getIndex(fProject);
assertNotNull(qtIndex);
IQObject qobj_B1 = qtIndex.findQObject(new String[]{ "B1" });
if (!isIndexOk("B1", qobj_B1))
return;
IQObject qobj_D1 = qtIndex.findQObject(new String[]{ "D1" });
assertNotNull(qobj_B1);
assertNotNull(qobj_D1);
Collection<IQObject> d1_bases = qobj_D1.getBases();
assertNotNull(d1_bases);
assertEquals(2, d1_bases.size());
Iterator<IQObject> iterator = d1_bases.iterator();
assertEquals(qobj_B1.getName(), iterator.next().getName());
assertEquals("B2", iterator.next().getName());
// D2 is not a QObject because it doesn't expand the Q_OBJECT macro
IQObject qobj_D2 = qtIndex.findQObject(new String[]{ "D2" });
assertNull(qobj_D2);
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2013 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.qt.tests;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.QtIndex;
public class QtIndexTests extends BaseQtTestCase {
private static final String Filename_testCache = "testCache.hh";
// #include "junit-QObject.hh"
// class B : public QObject
// {
// Q_OBJECT
// };
public void testLookup() throws Exception {
loadComment(Filename_testCache);
QtIndex qtIndex = QtIndex.getIndex(fProject);
assertNotNull(qtIndex);
// make sure the instance can be found
IQObject qobj1 = qtIndex.findQObject(new String[]{ "B" });
assertNotNull(qobj1);
assertEquals("B", qobj1.getName());
// make sure the instance is still found after the content changes
changeBDecl();
IQObject qobj2 = qtIndex.findQObject(new String[]{ "B" });
assertNotNull(qobj2);
assertEquals("B", qobj2.getName());
}
// #include "junit-QObject.hh"
// class B : public QObject
// {
// Q_OBJECT
// Q_PROPERTY(bool allowed READ isAllowed())
// };
public void changeBDecl() throws Exception {
loadComment(Filename_testCache);
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2013 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.qt.tests;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
/**
* The activator class controls the plug-in life cycle
*/
public class QtTestPlugin extends AbstractUIPlugin {
// The plug-in ID
public static final String PLUGIN_ID = "org.eclipse.cdt.qt.ui.tests"; //$NON-NLS-1$
// The shared instance
private static QtTestPlugin plugin;
/**
* The constructor
*/
public QtTestPlugin() {
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
*/
@Override
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
*/
@Override
public void stop(BundleContext context) throws Exception {
plugin = null;
super.stop(context);
}
/**
* Returns the shared instance
*
* @return the shared instance
*/
public static QtTestPlugin getDefault() {
return plugin;
}
}

View file

@ -1,3 +1,10 @@
/*
* Copyright (c) 2013 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.qt.ui;
import org.eclipse.ui.plugin.AbstractUIPlugin;
@ -24,6 +31,7 @@ public class QtUIPlugin extends AbstractUIPlugin {
* (non-Javadoc)
* @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
*/
@Override
public void start(BundleContext context) throws Exception {
super.start(context);
plugin = this;
@ -33,6 +41,7 @@ public class QtUIPlugin extends AbstractUIPlugin {
* (non-Javadoc)
* @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
*/
@Override
public void stop(BundleContext context) throws Exception {
plugin = null;
super.stop(context);