summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Eidsness2013-11-29 16:41:53 (EST)
committer Doug Schaefer2013-12-03 09:05:53 (EST)
commit16083fee1e0469dd327aefe11f62dbdb5ae89fd4 (patch)
tree568bd76631246a5597487eedcf4a5748d49107c8
parent0f482a80b2cfb9e1d8d44afef2a787ee04181dcd (diff)
downloadorg.eclipse.cdt-16083fee1e0469dd327aefe11f62dbdb5ae89fd4.zip
org.eclipse.cdt-16083fee1e0469dd327aefe11f62dbdb5ae89fd4.tar.gz
org.eclipse.cdt-16083fee1e0469dd327aefe11f62dbdb5ae89fd4.tar.bz2
Bug 422841: Add Q_CLASSINFO to the QtIndexrefs/changes/54/19154/2
Qt allows string-based key/value pairs to be inserted into QObject class definitions. E.g., class Q : public QObject { Q_OBJECT Q_CLASSINFO( "key1", "value1" ) }; The class info is accessible in the meta-object system. See: http://qt-project.org/doc/qt-4.8/qmetaclassinfo.html For more information. This patch adds the API to access these key/value pairs from the QtIndex. The values are stored in a single block in the PDOM record for the QObject. The API returns the value for a given key if it is found in the receiver QObject or any of its base classes. The API returns the first such value that is found. This patch also adds a test case for this functionality. Change-Id: Ie3f821a0c5f6f1347a0c0c6dafa184510ae26c29 Signed-off-by: Andrew Eidsness <eclipse@jfront.com> Reviewed-on: https://git.eclipse.org/r/19154 Tested-by: Hudson CI Reviewed-by: Doug Schaefer <dschaefer@qnx.com> IP-Clean: Doug Schaefer <dschaefer@qnx.com> Tested-by: Doug Schaefer <dschaefer@qnx.com>
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQObject.java6
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObject.java19
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QObjectName.java12
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTVisitor.java17
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMNodeType.java2
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQObject.java76
-rw-r--r--qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QObjectTests.java35
7 files changed, 166 insertions, 1 deletions
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
index 8d10aad..54678f8 100644
--- 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
@@ -39,6 +39,12 @@ public interface IQObject extends IQElement {
public List<IQObject> getBases();
/**
+ * Examines the Q_CLASSINFO expansions to return the value associated with the given
+ * key. Returns null if there isn't a Q_CLASSINFO for the given key.
+ */
+ public String getClassInfo(String key);
+
+ /**
* Returns an unsorted collection of all Q_ENUMS macro expansions within this QObject's class
* declaration.
* @see IQEnum
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
index e1c93ba..be86d8a 100644
--- 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
@@ -10,6 +10,7 @@ package org.eclipse.cdt.qt.internal.core.index;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
+import java.util.Map;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IField;
@@ -25,6 +26,7 @@ public class QObject implements IQObject {
private final QtPDOMQObject pdomQObject;
private final List<IQObject> bases;
private final List<IQEnum> enums;
+ private final Map<String, String> classInfos;
public QObject(QtIndexImpl qtIndex, CDTIndex cdtIndex, QtPDOMQObject pdomQObject) throws CoreException {
this.name = pdomQObject.getName();
@@ -34,6 +36,8 @@ public class QObject implements IQObject {
for(QtPDOMQObject base : pdomQObject.findBases())
this.bases.add(new QObject(qtIndex, cdtIndex, base));
+ this.classInfos = pdomQObject.getClassInfos();
+
this.enums = new ArrayList<IQEnum>();
for(IField field : pdomQObject.getFields())
if (field instanceof QtPDOMQEnum) {
@@ -58,6 +62,21 @@ public class QObject implements IQObject {
}
@Override
+ public String getClassInfo(String key) {
+ String value = classInfos.get(key);
+ if (value != null)
+ return value;
+
+ for(IQObject base : bases) {
+ value = base.getClassInfo(key);
+ if (value != null)
+ return value;
+ }
+
+ return null;
+ }
+
+ @Override
public Collection<IQEnum> getEnums() {
return enums;
}
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
index 32048dd..9699c79 100644
--- 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
@@ -7,6 +7,9 @@
*/
package org.eclipse.cdt.qt.internal.core.pdom;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
@@ -25,6 +28,7 @@ import org.eclipse.cdt.internal.core.dom.Linkage;
public class QObjectName extends ASTDelegatedName {
private final ICPPASTCompositeTypeSpecifier spec;
+ private final Map<String, String> classInfos = new LinkedHashMap<String, String>();
private IASTNode parent;
private ASTNodeProperty propertyInParent;
@@ -36,6 +40,14 @@ public class QObjectName extends ASTDelegatedName {
this.propertyInParent = delegate.getPropertyInParent();
}
+ public Map<String, String> getClassInfos() {
+ return classInfos;
+ }
+
+ public String addClassInfo(String key, String value) {
+ return classInfos.put(key, value);
+ }
+
@Override
protected IBinding createBinding() {
return new QtBinding(QtPDOMNodeType.QObject, this, spec.getName());
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
index 6aaca0e..b8a3b6d 100644
--- 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
@@ -41,6 +41,16 @@ public class QtASTVisitor extends ASTVisitor {
private static final Pattern declareFlagsRegex = Pattern.compile("^Q_DECLARE_FLAGS\\s*\\(\\s*([^\\s]+),\\s*([^\\s]+)\\s*\\)$");
+ /**
+ * A regular expression for scanning the Q_CLASSINFO expansion and extracting the
+ * expansion parameter key and value. It provides the following capture groups:
+ * <br>1 - the key
+ * <br>2 - the value
+ * <p>
+ * The key must not have embedded quotes.
+ */
+ private static final Pattern classInfoRegex = Pattern.compile("^Q_CLASSINFO\\s*\\(\\s*\"([^\"]+)\"\\s*,\\s*\"(.*)\"\\s*\\)$");
+
public QtASTVisitor(IIndexSymbols symbols, LocationMap locationMap) {
shouldVisitDeclSpecifiers = true;
@@ -141,6 +151,13 @@ public class QtASTVisitor extends ASTVisitor {
String enumName = m.group(2);
flagAliases.put(flagName, enumName);
}
+ } else if(QtKeywords.Q_CLASSINFO.equals(macroName)) {
+ Matcher m = classInfoRegex.matcher(expansion.getRawSignature());
+ if (m.matches()) {
+ String key = m.group(1);
+ String value = m.group(2);
+ qobjName.addClassInfo(key, value);
+ }
}
}
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
index 182333c..4db7344 100644
--- 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
@@ -26,7 +26,7 @@ public enum QtPDOMNodeType {
* <p>
* The version is needed because ordinals for these enumerators are written to the file.
*/
- public static final int VERSION = 1;
+ public static final int VERSION = 2;
public static QtPDOMNodeType forType(int version, int type) {
// Nothing has been deleted or replaced yet, so the version is ignored.
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
index 8a6b2a9..21222d1 100644
--- 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
@@ -9,8 +9,11 @@ package org.eclipse.cdt.qt.internal.core.pdom;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
+import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IField;
@@ -18,6 +21,8 @@ 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.Database;
+import org.eclipse.cdt.internal.core.pdom.db.IString;
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;
@@ -37,6 +42,7 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
private static int offsetInitializer = QtPDOMBinding.Field.Last.offset;
protected static enum Field {
Children(4 /* From PDOMNodeLinkedList.RECORD_SIZE, which is protected */),
+ ClassInfos(Database.PTR_SIZE),
Last(0);
private final int offset;
@@ -61,6 +67,76 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
public QtPDOMQObject(QtPDOMLinkage linkage, QtBinding binding) throws CoreException {
super(linkage, null, binding);
children = new PDOMNodeLinkedList(linkage, Field.Children.getRecord(record));
+
+ IASTName qtName = binding.getQtName();
+ if (qtName instanceof QObjectName)
+ setClassInfos(((QObjectName) qtName).getClassInfos());
+ }
+
+ public void setClassInfos(Map<String, String> classInfos) throws CoreException {
+
+ // ClassInfo was not supported before version 2.
+ if (getQtLinkage().getVersion() < 2)
+ return;
+
+ // Delete all entries that are currently in the list.
+ long block = getDB().getRecPtr(Field.ClassInfos.getRecord(record));
+ if (block != 0) {
+ int numEntries = getDB().getInt(block);
+ for(long b = block + Database.INT_SIZE, end = block + (numEntries * 2 * Database.PTR_SIZE); b < end; b += Database.PTR_SIZE)
+ getDB().getString(b).delete();
+ getDB().free(block);
+ }
+
+ // Clear the pointer if the incoming map is empty.
+ if (classInfos.isEmpty()) {
+ getDB().putRecPtr(Field.ClassInfos.getRecord(record), 0);
+ return;
+ }
+
+ // Otherwise create a block large enough to hold the incoming list and then populate it.
+ block = getDB().malloc(Database.INT_SIZE + (classInfos.size() * 2 * Database.PTR_SIZE));
+ getDB().putInt(block, classInfos.size());
+
+ long b = block + Database.INT_SIZE;
+ for(Map.Entry<String, String> classInfo : classInfos.entrySet()) {
+ IString key = getDB().newString(classInfo.getKey());
+ IString val = getDB().newString(classInfo.getValue());
+
+ getDB().putRecPtr(b, key.getRecord()); b += Database.PTR_SIZE;
+ getDB().putRecPtr(b, val.getRecord()); b += Database.PTR_SIZE;
+ }
+
+ // Put the new block into the PDOM.
+ getDB().putRecPtr(Field.ClassInfos.getRecord(record), block);
+ }
+
+ public Map<String, String> getClassInfos() throws CoreException {
+ Map<String, String> classInfos = new LinkedHashMap<String, String>();
+
+ // ClassInfo was not supported before version 2.
+ if (getQtLinkage().getVersion() < 2)
+ return classInfos;
+
+ long block = getDB().getRecPtr(Field.ClassInfos.getRecord(record));
+ if (block != 0) {
+ int numEntries = getDB().getInt(block);
+ block += Database.INT_SIZE;
+
+ for(long end = block + (numEntries * 2 * Database.PTR_SIZE); block < end; /* in loop body */) {
+ long rec = getDB().getRecPtr(block);
+ IString key = getDB().getString(rec);
+ block += Database.PTR_SIZE;
+
+ rec = getDB().getRecPtr(block);
+ IString val = getDB().getString(rec);
+ block += Database.PTR_SIZE;
+
+ classInfos.put(key.getString(), val.getString());
+ }
+ }
+
+ return classInfos;
}
@Override
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
index c65bc80..9dbde17 100644
--- 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
@@ -48,6 +48,41 @@ public class QObjectTests extends BaseQtTestCase {
assertNull(qobj_D2);
}
+ // #include "junit-QObject.hh"
+ // class B : public QObject
+ // {
+ // Q_OBJECT
+ // Q_CLASSINFO( "key1", "value1" )
+ // Q_CLASSINFO( "key2", "value\"2" )
+ // public:
+ // bool isAllowed() const { return false; }
+ // };
+ // class D : public B
+ // {
+ // Q_OBJECT
+ // Q_CLASSINFO( "key2", "overridden value" )
+ // public:
+ // bool isAllowed() const { return false; }
+ // };
+ public void testClassInfos() throws Exception {
+ loadComment("classinfos.hh");
+
+ QtIndex qtIndex = QtIndex.getIndex(fProject);
+ assertNotNull(qtIndex);
+
+ IQObject qobj_b = qtIndex.findQObject(new String[]{ "B" });
+ if (!isIndexOk("B", qobj_b))
+ return;
+ assertNotNull(qobj_b);
+ assertEquals("value1", qobj_b.getClassInfo("key1"));
+ assertEquals("value\\\"2", qobj_b.getClassInfo("key2"));
+
+ IQObject qobj_d = qtIndex.findQObject(new String[]{ "D" });
+ assertNotNull(qobj_d);
+ assertEquals("value1", qobj_d.getClassInfo("key1")); // inherited
+ assertEquals("overridden value", qobj_d.getClassInfo("key2"));
+ }
+
// #include "junit-QObject.hh"
// template <typename T> class QList {};
// class QString {};