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 8d10aadefef..54678f8807c 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 @@ -38,6 +38,12 @@ public interface IQObject extends IQElement { */ public List 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. 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 e1c93bad2cb..be86d8ad442 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 bases; private final List enums; + private final Map 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(); for(IField field : pdomQObject.getFields()) if (field instanceof QtPDOMQEnum) { @@ -57,6 +61,21 @@ public class QObject implements IQObject { return bases; } + @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 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 32048dd88c4..9699c798fd9 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 classInfos = new LinkedHashMap(); private IASTNode parent; private ASTNodeProperty propertyInParent; @@ -36,6 +40,14 @@ public class QObjectName extends ASTDelegatedName { this.propertyInParent = delegate.getPropertyInParent(); } + public Map 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 6aaca0e4ff4..b8a3b6d6806 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: + *
1 - the key + *
2 - the value + *

+ * 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 182333c9fc7..4db73443c96 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 { *

* 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 8a6b2a9c176..21222d18068 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 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 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 getClassInfos() throws CoreException { + Map classInfos = new LinkedHashMap(); + + // 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 c65bc80146b..9dbde17c3b3 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 class QList {}; // class QString {};