+
@@ -53,12 +52,11 @@
-
+ point="org.eclipse.cdt.core.PDOMASTProcessor"
+ id="qt.PDOMASTProcessor"
+ name="Qt PDOM AST Processor">
+
@@ -66,6 +64,11 @@
-
+
+
+
+
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 2e514ebfbeb..cefcfc148b1 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 f11e1b22b9e..cfe7a36d479 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;
+
+ public static QtPlugin getDefault() {
+ return instance;
+ }
+
+ public QtPlugin() {
+ instance = this;
+ }
+
+ public static IStatus info(String msg) {
+ return new Status(IStatus.INFO, ID, msg);
}
- static QtPlugin getDefault() {
- return INSTANCE;
+ public static IStatus error(String msg) {
+ return error(msg, null);
}
- /*
- * (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 static IStatus error(String msg, Throwable e) {
+ return new Status(IStatus.ERROR, ID, msg, e);
}
- /*
- * (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 log(String e) {
+ return log(IStatus.INFO, e, null);
}
- public static void log(String e) {
- log(IStatus.INFO, e, null);
+ public static IStatus log(Throwable e) {
+ String msg = e.getMessage();
+ return msg == null ? log("Error", e) : log("Error: " + 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 message, Throwable e) {
+ return log(IStatus.ERROR, message, e);
}
- public static void log(String message, Throwable e) {
- Throwable nestedException;
- if (e instanceof CModelException && (nestedException = ((CModelException)e).getException()) != null) {
- e = nestedException;
- }
- log(IStatus.ERROR, message, e);
+ public static IStatus log(int code, String msg, Throwable e) {
+ IStatus status = new Status(code, ID, msg, e);
+ instance.getLog().log(status);
+ return status;
}
-
- public static void log(int code, String msg, Throwable e) {
- getDefault().getLog().log(new Status(code, ID, msg, e));
- }
-
}
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 00000000000..cdabffdddb9
--- /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 00000000000..3cbe4a0f4cc
--- /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:
+ *
+ * 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 };
+ *
+ * The list of bases for D will contain B1 and B2, but not B3 or T.
+ *
+ * The list will be ordered as in the C++ code and will include only the directly declared
+ * base classes.
+ */
+ public List 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 00000000000..90bdde99d2c
--- /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 06c2f6f08d7..00000000000
--- 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:
- *
- *
- * class T
- * {
- * private:
- * Q_SLOT void some_slot();
- *
- * signals:
- * void some_signal();
- * };
- *
- *
- * 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 00000000000..c187dea5881
--- /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 {
+ /**
+ * A method that performs the lookup within the CDT index. The read-lock will
+ * be acquired before invoking this method.
+ *
+ * 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.
+ */
+ 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 get(Accessor 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 00000000000..1ba7951e818
--- /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 bases;
+
+ public QObject(QtIndexImpl qtIndex, CDTIndex cdtIndex, QtPDOMQObject pdomQObject) throws CoreException {
+ this.name = pdomQObject.getName();
+ this.pdomQObject = pdomQObject;
+
+ this.bases = new ArrayList();
+ 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 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 00000000000..7d95d6e1515
--- /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 QtVersionAccessor = new CDTIndex.Accessor() {
+ @Override
+ public QtVersion access(IIndex index) throws CoreException {
+ // Multiple macros might be found, sort the values and choose the highest version.
+ SortedSet versions = new TreeSet();
+ 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 00000000000..d127c133a8e
--- /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 {
+
+ 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 00000000000..d102a466474
--- /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 00000000000..e2b2c190ae9
--- /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 00000000000..32048dd88c4
--- /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 00000000000..c1cda5330b0
--- /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 00000000000..1537f83dbde
--- /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 00000000000..ba9b5689d1d
--- /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 00000000000..e5548905467
--- /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 00000000000..ebd0968ddf2
--- /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 00000000000..2048afebb6e
--- /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.
+ *
+ * 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 00000000000..8a6b2a9c176
--- /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 findBases() throws CoreException {
+ IBinding cppBinding = getCppBinding();
+ if (!(cppBinding instanceof ICPPClassType))
+ return Collections.emptyList();
+
+ List bases = new ArrayList();
+ 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 collector = new QtPDOMVisitor.All(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 finder = new QtPDOMVisitor.Find(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 00000000000..3dec16f2114
--- /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 extends QtPDOMVisitor {
+
+ private final Class cls;
+ public final ArrayList list = new ArrayList();
+
+ public All(Class 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 extends QtPDOMVisitor {
+
+ private final Class cls;
+ private final IFilter filter;
+
+ public T element;
+
+ public Find(Class 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 00000000000..ad32c83a788
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.tests/.classpath
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/qt/org.eclipse.cdt.qt.tests/.project b/qt/org.eclipse.cdt.qt.tests/.project
new file mode 100644
index 00000000000..b40b063abdd
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.tests/.project
@@ -0,0 +1,28 @@
+
+
+ org.eclipse.cdt.qt.tests
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.pde.ManifestBuilder
+
+
+
+
+ org.eclipse.pde.SchemaBuilder
+
+
+
+
+
+ org.eclipse.pde.PluginNature
+ org.eclipse.jdt.core.javanature
+
+
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 00000000000..2e2cf2a9d36
--- /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 00000000000..34d2e4d2dad
--- /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 00000000000..a062abd1423
--- /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 00000000000..b2915ff738b
--- /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 { 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 int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
+ // template int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName);
+ // template 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.
+ *
+ * - loadComment must be called from a public method
+ * - loadComment must be called from a method that does not accept parameters
+ *
+ */
+ 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 00000000000..a3011bc03fd
--- /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 d1_bases = qobj_D1.getBases();
+ assertNotNull(d1_bases);
+ assertEquals(2, d1_bases.size());
+ Iterator 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 00000000000..e4fd1852588
--- /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 00000000000..13222320380
--- /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 d4b781e9e8b..a8fab56b811 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);