mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-23 22:52: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:
parent
0f482a80b2
commit
16083fee1e
7 changed files with 166 additions and 1 deletions
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {};
|
||||||
|
|
Loading…
Add table
Reference in a new issue