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:
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();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
|
|
@ -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) {
|
||||
|
@ -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<IQEnum> getEnums() {
|
||||
return enums;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {};
|
||||
|
|
Loading…
Add table
Reference in a new issue