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

Bug 422841: Add Q_CLASSINFO to the QtIndex

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>
This commit is contained in:
Andrew Eidsness 2013-11-29 16:41:53 -05:00 committed by Doug Schaefer
parent 0f482a80b2
commit 16083fee1e
7 changed files with 166 additions and 1 deletions

View file

@ -38,6 +38,12 @@ public interface IQObject extends IQElement {
*/ */
public List<IQObject> getBases(); 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 * Returns an unsorted collection of all Q_ENUMS macro expansions within this QObject's class
* declaration. * declaration.

View file

@ -10,6 +10,7 @@ package org.eclipse.cdt.qt.internal.core.index;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.dom.ast.IBinding; import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IField; import org.eclipse.cdt.core.dom.ast.IField;
@ -25,6 +26,7 @@ public class QObject implements IQObject {
private final QtPDOMQObject pdomQObject; private final QtPDOMQObject pdomQObject;
private final List<IQObject> bases; private final List<IQObject> bases;
private final List<IQEnum> enums; private final List<IQEnum> enums;
private final Map<String, String> classInfos;
public QObject(QtIndexImpl qtIndex, CDTIndex cdtIndex, QtPDOMQObject pdomQObject) throws CoreException { public QObject(QtIndexImpl qtIndex, CDTIndex cdtIndex, QtPDOMQObject pdomQObject) throws CoreException {
this.name = pdomQObject.getName(); this.name = pdomQObject.getName();
@ -34,6 +36,8 @@ public class QObject implements IQObject {
for(QtPDOMQObject base : pdomQObject.findBases()) for(QtPDOMQObject base : pdomQObject.findBases())
this.bases.add(new QObject(qtIndex, cdtIndex, base)); this.bases.add(new QObject(qtIndex, cdtIndex, base));
this.classInfos = pdomQObject.getClassInfos();
this.enums = new ArrayList<IQEnum>(); this.enums = new ArrayList<IQEnum>();
for(IField field : pdomQObject.getFields()) for(IField field : pdomQObject.getFields())
if (field instanceof QtPDOMQEnum) { if (field instanceof QtPDOMQEnum) {
@ -57,6 +61,21 @@ public class QObject implements IQObject {
return bases; 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 @Override
public Collection<IQEnum> getEnums() { public Collection<IQEnum> getEnums() {
return enums; return enums;

View file

@ -7,6 +7,9 @@
*/ */
package org.eclipse.cdt.qt.internal.core.pdom; 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.ILinkage;
import org.eclipse.cdt.core.dom.ast.ASTNodeProperty; import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
import org.eclipse.cdt.core.dom.ast.ASTVisitor; 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 { public class QObjectName extends ASTDelegatedName {
private final ICPPASTCompositeTypeSpecifier spec; private final ICPPASTCompositeTypeSpecifier spec;
private final Map<String, String> classInfos = new LinkedHashMap<String, String>();
private IASTNode parent; private IASTNode parent;
private ASTNodeProperty propertyInParent; private ASTNodeProperty propertyInParent;
@ -36,6 +40,14 @@ public class QObjectName extends ASTDelegatedName {
this.propertyInParent = delegate.getPropertyInParent(); this.propertyInParent = delegate.getPropertyInParent();
} }
public Map<String, String> getClassInfos() {
return classInfos;
}
public String addClassInfo(String key, String value) {
return classInfos.put(key, value);
}
@Override @Override
protected IBinding createBinding() { protected IBinding createBinding() {
return new QtBinding(QtPDOMNodeType.QObject, this, spec.getName()); return new QtBinding(QtPDOMNodeType.QObject, this, spec.getName());

View file

@ -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*\\)$"); 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) { public QtASTVisitor(IIndexSymbols symbols, LocationMap locationMap) {
shouldVisitDeclSpecifiers = true; shouldVisitDeclSpecifiers = true;
@ -141,6 +151,13 @@ public class QtASTVisitor extends ASTVisitor {
String enumName = m.group(2); String enumName = m.group(2);
flagAliases.put(flagName, enumName); 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);
}
} }
} }

View file

@ -26,7 +26,7 @@ public enum QtPDOMNodeType {
* <p> * <p>
* The version is needed because ordinals for these enumerators are written to the file. * 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) { public static QtPDOMNodeType forType(int version, int type) {
// Nothing has been deleted or replaced yet, so the version is ignored. // Nothing has been deleted or replaced yet, so the version is ignored.

View file

@ -9,8 +9,11 @@ package org.eclipse.cdt.qt.internal.core.pdom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List; 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.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType; import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IField; 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.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase; import org.eclipse.cdt.core.dom.ast.cpp.ICPPBase;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; 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.db.PDOMNodeLinkedList;
import org.eclipse.cdt.internal.core.pdom.dom.IPDOMBinding; import org.eclipse.cdt.internal.core.pdom.dom.IPDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode; 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; private static int offsetInitializer = QtPDOMBinding.Field.Last.offset;
protected static enum Field { protected static enum Field {
Children(4 /* From PDOMNodeLinkedList.RECORD_SIZE, which is protected */), Children(4 /* From PDOMNodeLinkedList.RECORD_SIZE, which is protected */),
ClassInfos(Database.PTR_SIZE),
Last(0); Last(0);
private final int offset; private final int offset;
@ -61,6 +67,76 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
public QtPDOMQObject(QtPDOMLinkage linkage, QtBinding binding) throws CoreException { public QtPDOMQObject(QtPDOMLinkage linkage, QtBinding binding) throws CoreException {
super(linkage, null, binding); super(linkage, null, binding);
children = new PDOMNodeLinkedList(linkage, Field.Children.getRecord(record)); 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 @Override

View file

@ -48,6 +48,41 @@ public class QObjectTests extends BaseQtTestCase {
assertNull(qobj_D2); 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" // #include "junit-QObject.hh"
// template <typename T> class QList {}; // template <typename T> class QList {};
// class QString {}; // class QString {};