summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Eidsness2013-11-29 08:28:51 (EST)
committerDoug Schaefer2013-11-29 14:43:41 (EST)
commitfccbec7b0d8bf66ec8bb1110a993df29a2045da4 (patch)
tree91fefc8e8c65060a4a03936510d78db645ae269a
parent8610899d03381c68187d18e8c0cc3720e4af4ce4 (diff)
downloadorg.eclipse.cdt-fccbec7b0d8bf66ec8bb1110a993df29a2045da4.zip
org.eclipse.cdt-fccbec7b0d8bf66ec8bb1110a993df29a2045da4.tar.gz
org.eclipse.cdt-fccbec7b0d8bf66ec8bb1110a993df29a2045da4.tar.bz2
Bug 422841: Initial implementation of QtIndexrefs/changes/13/19113/4
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>
-rw-r--r--core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ILinkage.java12
-rw-r--r--core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/Linkage.java8
-rw-r--r--qt/org.eclipse.cdt.qt.core/plugin.xml21
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java16
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java85
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQElement.java20
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQObject.java39
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QtIndex.java65
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtSignalSlotTagger.java230
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/CDTIndex.java62
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObject.java47
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtFactory.java122
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtIndexImpl.java66
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTDelegatedName.java280
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/PDOMQtLinkageFactory.java33
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QObjectName.java98
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTVisitor.java68
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtBinding.java89
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMASTProcessor.java24
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java98
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMLinkage.java245
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMNodeType.java52
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQObject.java182
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMVisitor.java109
-rw-r--r--qt/org.eclipse.cdt.qt.tests/.classpath7
-rw-r--r--qt/org.eclipse.cdt.qt.tests/.project28
-rw-r--r--qt/org.eclipse.cdt.qt.tests/META-INF/MANIFEST.MF19
-rw-r--r--qt/org.eclipse.cdt.qt.tests/build.properties4
-rw-r--r--qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/AllQtTests.java21
-rw-r--r--qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/BaseQtTestCase.java167
-rw-r--r--qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QObjectTests.java49
-rw-r--r--qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtIndexTests.java49
-rw-r--r--qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtTestPlugin.java59
-rw-r--r--qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/ui/QtUIPlugin.java11
34 files changed, 2195 insertions, 290 deletions
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ILinkage.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ILinkage.java
index 05737e8..d930e04 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ILinkage.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/core/dom/ILinkage.java
@@ -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,7 +42,12 @@ 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.
*/
diff --git a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/Linkage.java b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/Linkage.java
index 4414f2d..f178e9f 100644
--- a/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/Linkage.java
+++ b/core/org.eclipse.cdt.core/parser/org/eclipse/cdt/internal/core/dom/Linkage.java
@@ -20,10 +20,11 @@ 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);
-
- private static final ILinkage[] LINKAGES= { C_LINKAGE, CPP_LINKAGE, FORTRAN_LINKAGE, OBJC_LINKAGE };
+ 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, QT_LINKAGE };
private static final ILinkage[] INDEX_LINKAGES= { C_LINKAGE, CPP_LINKAGE, FORTRAN_LINKAGE };
-
+
public static final ILinkage[] getIndexerLinkages() {
return INDEX_LINKAGES;
}
@@ -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$
}
diff --git a/qt/org.eclipse.cdt.qt.core/plugin.xml b/qt/org.eclipse.cdt.qt.core/plugin.xml
index eb99e06..7b3ebd7 100644
--- a/qt/org.eclipse.cdt.qt.core/plugin.xml
+++ b/qt/org.eclipse.cdt.qt.core/plugin.xml
@@ -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>
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java
index 2e514eb..cefcfc1 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtKeywords.java
@@ -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";
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java
index f11e1b2..cfe7a36 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java
@@ -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;
+ /**
+ * 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.
+ */
+ public static final QualifiedName QTINDEX_PROP_NAME = new QualifiedName(ID, "qtindex");
- static BundleContext getContext() {
- return context;
- }
+ private static QtPlugin instance;
- static QtPlugin getDefault() {
- return INSTANCE;
- }
+ public static QtPlugin getDefault() {
+ return instance;
+ }
- /*
- * (non-Javadoc)
- * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
- */
- @Override
- public void start(BundleContext bundleContext) throws Exception {
- INSTANCE = this;
- QtPlugin.context = bundleContext;
- QMakeProjectInfo.start();
+ public QtPlugin() {
+ instance = this;
+ }
+
+ public static IStatus info(String msg) {
+ return new Status(IStatus.INFO, ID, msg);
}
- /*
- * (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 static IStatus error(String msg) {
+ return error(msg, null);
}
- public static void log(String e) {
- log(IStatus.INFO, e, null);
+ public static IStatus error(String msg, Throwable e) {
+ return new Status(IStatus.ERROR, ID, msg, e);
}
- public static void log(Throwable e) {
- String msg= e.getMessage();
- if (msg == null) {
- log("Error", e); //$NON-NLS-1$
- } else {
- log("Error: " + msg, e); //$NON-NLS-1$
- }
+ public static IStatus log(String e) {
+ return log(IStatus.INFO, e, null);
}
- 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(Throwable e) {
+ String msg = e.getMessage();
+ return msg == null ? log("Error", e) : log("Error: " + msg, e);
}
- public static void log(int code, String msg, Throwable e) {
- getDefault().getLog().log(new Status(code, ID, msg, e));
+ public static IStatus log(String message, Throwable e) {
+ return log(IStatus.ERROR, message, 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;
+ }
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQElement.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQElement.java
new file mode 100644
index 0000000..cdabffd
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQElement.java
@@ -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();
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQObject.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQObject.java
new file mode 100644
index 0000000..3cbe4a0
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQObject.java
@@ -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();
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QtIndex.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QtIndex.java
new file mode 100644
index 0000000..90bdde9
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/QtIndex.java
@@ -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);
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtSignalSlotTagger.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtSignalSlotTagger.java
deleted file mode 100644
index 06c2f6f..0000000
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/QtSignalSlotTagger.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/CDTIndex.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/CDTIndex.java
new file mode 100644
index 0000000..c187dea
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/CDTIndex.java
@@ -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;
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObject.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObject.java
new file mode 100644
index 0000000..1ba7951
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObject.java
@@ -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;
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtFactory.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtFactory.java
new file mode 100644
index 0000000..7d95d6e
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtFactory.java
@@ -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());
+ }
+ };
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtIndexImpl.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtIndexImpl.java
new file mode 100644
index 0000000..d127c13
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QtIndexImpl.java
@@ -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;
+ }
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTDelegatedName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTDelegatedName.java
new file mode 100644
index 0000000..d102a46
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTDelegatedName.java
@@ -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();
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/PDOMQtLinkageFactory.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/PDOMQtLinkageFactory.java
new file mode 100644
index 0000000..e2b2c19
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/PDOMQtLinkageFactory.java
@@ -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);
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QObjectName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QObjectName.java
new file mode 100644
index 0000000..32048dd
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QObjectName.java
@@ -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);
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTVisitor.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTVisitor.java
new file mode 100644
index 0000000..c1cda53
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTVisitor.java
@@ -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);
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtBinding.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtBinding.java
new file mode 100644
index 0000000..1537f83
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtBinding.java
@@ -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;
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMASTProcessor.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMASTProcessor.java
new file mode 100644
index 0000000..ba9b568
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMASTProcessor.java
@@ -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;
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java
new file mode 100644
index 0000000..e554890
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java
@@ -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);
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMLinkage.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMLinkage.java
new file mode 100644
index 0000000..ebd0968
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMLinkage.java
@@ -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;
+ }
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMNodeType.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMNodeType.java
new file mode 100644
index 0000000..2048afe
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMNodeType.java
@@ -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;
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQObject.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQObject.java
new file mode 100644
index 0000000..8a6b2a9
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQObject.java
@@ -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();
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMVisitor.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMVisitor.java
new file mode 100644
index 0000000..3dec16f
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMVisitor.java
@@ -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 {
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.tests/.classpath b/qt/org.eclipse.cdt.qt.tests/.classpath
new file mode 100644
index 0000000..ad32c83
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.tests/.classpath
@@ -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>
diff --git a/qt/org.eclipse.cdt.qt.tests/.project b/qt/org.eclipse.cdt.qt.tests/.project
new file mode 100644
index 0000000..b40b063
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.tests/.project
@@ -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>
diff --git a/qt/org.eclipse.cdt.qt.tests/META-INF/MANIFEST.MF b/qt/org.eclipse.cdt.qt.tests/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..2e2cf2a
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.tests/META-INF/MANIFEST.MF
@@ -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
diff --git a/qt/org.eclipse.cdt.qt.tests/build.properties b/qt/org.eclipse.cdt.qt.tests/build.properties
new file mode 100644
index 0000000..34d2e4d
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.tests/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/AllQtTests.java b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/AllQtTests.java
new file mode 100644
index 0000000..a062abd
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/AllQtTests.java
@@ -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);
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/BaseQtTestCase.java b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/BaseQtTestCase.java
new file mode 100644
index 0000000..b2915ff
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/BaseQtTestCase.java
@@ -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);
+ }
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QObjectTests.java b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QObjectTests.java
new file mode 100644
index 0000000..a3011bc
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QObjectTests.java
@@ -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);
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtIndexTests.java b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtIndexTests.java
new file mode 100644
index 0000000..e4fd185
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtIndexTests.java
@@ -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);
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtTestPlugin.java b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtTestPlugin.java
new file mode 100644
index 0000000..1322232
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QtTestPlugin.java
@@ -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;
+ }
+
+}
diff --git a/qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/ui/QtUIPlugin.java b/qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/ui/QtUIPlugin.java
index d4b781e..a8fab56 100644
--- a/qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/ui/QtUIPlugin.java
+++ b/qt/org.eclipse.cdt.qt.ui/src/org/eclipse/cdt/qt/ui/QtUIPlugin.java
@@ -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;
@@ -13,7 +20,7 @@ public class QtUIPlugin extends AbstractUIPlugin {
// The shared instance
private static QtUIPlugin plugin;
-
+
/**
* The constructor
*/
@@ -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);