summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Eidsness2013-12-04 08:00:18 (EST)
committer Doug Schaefer2013-12-10 20:39:41 (EST)
commit2d9f3f10287d2874de817e7698775e5b1fcd1e6b (patch)
tree1231cc17a22bce6716a8cd83135694dc7c62b493
parent431dff56713c317965695beefafa9f87ce93971a (diff)
downloadorg.eclipse.cdt-2d9f3f10287d2874de817e7698775e5b1fcd1e6b.zip
org.eclipse.cdt-2d9f3f10287d2874de817e7698775e5b1fcd1e6b.tar.gz
org.eclipse.cdt-2d9f3f10287d2874de817e7698775e5b1fcd1e6b.tar.bz2
Bug 422841: Add Q_PROPERTY to the QtIndexrefs/changes/02/19602/4
This extends IQObject so that it can return the list of Q_PROPERTY expansions. Each Q_PROPERTY is represented by the new IQProperty. The attributes of the property are stored in IQProperty.Attribute. Where applicable, the attributes insert a reference from the associated C++ binding. This means that the Q_PROPERTY expansion will be included in the list of references for the C++ binding. This also simplifies the process for adding new PDOMBindings to the Qt linkage. New instances are stored in an implementation of IQtASTName and able to directly create and return a new QtPDOMBinding. This avoids creating three parallel inheritance hierachies (compared to the previous Qt PDOM implementations). The patch includes test cases to check handling of Q_PROPERTY expansions. Change-Id: I7d256d1a938d24a9eb726c472fb150a02f26eb32 Signed-off-by: Andrew Eidsness <eclipse@jfront.com> Reviewed-on: https://git.eclipse.org/r/19602 Tested-by: Hudson CI Reviewed-by: Doug Schaefer <dschaefer@qnx.com> IP-Clean: Doug Schaefer <dschaefer@qnx.com>
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java5
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQObject.java53
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQProperty.java164
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/AbstractQField.java46
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObject.java34
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObjectMembers.java86
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QProperty.java213
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTDelegatedName.java54
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTNameReference.java109
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/AbstractQObjectFieldName.java123
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/IQtASTName.java21
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/IQtPDOMCodec.java39
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QObjectName.java19
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtASTVisitor.java213
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtBinding.java89
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtEnumName.java113
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMArray.java127
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java63
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMLinkage.java151
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMNodeType.java8
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMProperty.java245
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQEnum.java38
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQObject.java219
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPropertyAttributeName.java32
-rw-r--r--qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPropertyName.java60
-rw-r--r--qt/org.eclipse.cdt.qt.tests/src/org/eclipse/cdt/qt/tests/QObjectTests.java275
26 files changed, 2095 insertions, 504 deletions
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java
index b4f1de5..276f4dd 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/QtPlugin.java
@@ -8,6 +8,7 @@
package org.eclipse.cdt.qt.core;
import org.eclipse.cdt.core.model.CModelException;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.QualifiedName;
@@ -40,6 +41,10 @@ public class QtPlugin extends Plugin {
instance = this;
}
+ public static CoreException coreException(String msg) {
+ return new CoreException(new Status(IStatus.INFO, ID, msg));
+ }
+
public static IStatus info(String msg) {
return new Status(IStatus.INFO, ID, msg);
}
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 54678f8..17670e2 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
@@ -16,6 +16,54 @@ import java.util.List;
* @see IQMethod
*/
public interface IQObject extends IQElement {
+
+ /**
+ * The interface to be implemented by elements that can be returned as members of an
+ * implementation of {@link IQObject}.
+ */
+ public static interface IMember {
+ /**
+ * Return the QObject class that declares this member. Does not return null.
+ */
+ public IQObject getOwner();
+
+ /**
+ * Returns true if it is <strong>*possible*</strong> for this member and the parameter
+ * to override each the other. A true result indicates that <strong>*if*</strong> the members
+ * owner's are related by inheritance then one will override the other; the implementation
+ * does not check that there actually is such an inheritance relationship.
+ */
+ public boolean isOverride(IMember member);
+ }
+
+ /**
+ * A wrapper for unmodifiable collections of members of a class. Accessors provide filtered
+ * views of the member list.
+ *
+ * @see #all()
+ * @see #locals()
+ * @see #withoutOverrides()
+ */
+ public static interface IMembers<T extends IMember> {
+ /**
+ * Returns an unmodifiable collection with all locally declared, inherited, and overridden
+ * members. Does not return null.
+ */
+ public Collection<T> all();
+
+ /**
+ * Returns an unmodifiable collection with only the members that are locally declared in the
+ * source class. Does not return null.
+ */
+ public Collection<T> locals();
+
+ /**
+ * Returns an unmodifiable collection of all locally declared and inherited members with the
+ * overridden members filtered out. Does not return null.
+ */
+ public Collection<T> withoutOverrides();
+ }
+
/**
* Returns the name of the class.
*/
@@ -39,6 +87,11 @@ public interface IQObject extends IQElement {
public List<IQObject> getBases();
/**
+ * Returns the expansions of the Q_PROPERTY macro. Does not return null.
+ */
+ public IMembers<IQProperty> getProperties();
+
+ /**
* 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.
*/
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQProperty.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQProperty.java
new file mode 100644
index 0000000..663ec7b
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/core/index/IQProperty.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2013 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.qt.core.index;
+
+/**
+ * A container for things declared as Q_PROPERTY within a subclass of QObject. In Qt 4.8,
+ * Q_PROPERTY looks like:
+ * <pre>
+ * Q_PROPERTY( type name
+ * READ getFunction
+ * [WRITE setFunction]
+ * [RESET resetFunction]
+ * [NOTIFY notifySignal]
+ * [REVISION int]
+ * [DESIGNABLE bool]
+ * [SCRIPTABLE bool]
+ * [STORED bool]
+ * [USER bool]
+ * [CONSTANT]
+ * [FINAL] )
+ * </pre>
+ * This interface provides structured access to the elements of the Q_PROPERTY expansion.
+ */
+public interface IQProperty extends IQObject.IMember {
+
+ /**
+ * The attributes that were defined in Qt 4.8. This is the full set of attributes
+ * that will be parsed from Q_PROPERTY expansions.
+ */
+ public static enum Attribute {
+ // This enum is used when scanning Q_PROPERTY expansions, only attributes listed here
+ // will be extracted from the expansion. This enum should be expanded with new values
+ // as needed (based on the version of Qt). See QProperty#scanAttributes.
+ READ("getFunction"),
+ WRITE("setFunction"),
+ RESET("resetFunction"),
+ NOTIFY("notifySignal"),
+ REVISION("int"),
+ DESIGNABLE("bool"),
+ SCRIPTABLE("bool"),
+ STORED("bool"),
+ USER("bool"),
+ CONSTANT(null),
+ FINAL(null);
+
+ /**
+ * A string containing the C++ identifier for this attribute.
+ */
+ public final String identifier = toString();
+
+ /**
+ * Stores the attribute parameter name if it exists and null for attributes
+ * without parameters. The parameter name comes from the Qt documentation
+ * and is only intended for documentation.
+ */
+ public final String paramName;
+
+ /**
+ * True when this attribute is expected to have a value and false otherwise.
+ */
+ public final boolean hasValue;
+
+ private Attribute(String paramName) {
+ this.paramName = paramName;
+ this.hasValue = paramName != null;
+ }
+
+ /**
+ * A convenience method to access the value of a property through this
+ * enum, e.g.,
+ * <pre>
+ * IQProperty qprop;
+ * String readFunction = IQProperty.Attribute.READ.valueId( qprop );
+ * </pre>
+ * Returns null if there is no associated value in the given property.
+ */
+ public String valueIn(IQProperty qprop) {
+ return qprop == null ? null : qprop.getValue(this);
+ }
+ }
+
+ /**
+ * Returns the type of the property. This is the first field in the Q_PROPERTY expansion.
+ */
+ public String getType();
+
+ /**
+ * Returns the name of the property. This is the second field in the Q_PROPERTY expansion.
+ */
+ public String getName();
+
+ /**
+ * Return the value of the attribute associated with the given key. E.g., in
+ * <pre>
+ * Q_PROPERTY( bool allowed READ isAllowed )
+ * <pre>
+ * The parameter Attribute.READ would return "isAllowed".
+ *
+ * Returns null if the given key is not described in the property.
+ */
+ public String getValue(Attribute attr);
+
+ /**
+ * Return the value of READ or null if READ is not described in the property.
+ */
+ public String getReadMethodName();
+
+
+ /**
+ * Return the value of WRITE or null if WRITE is not described in the property.
+ */
+ public String getWriteMethodName();
+
+ /**
+ * Return the value of RESET or null if RESET is not described in the property.
+ */
+ public String getResetMethodName();
+
+ /**
+ * Return the value of NOTIFY or null if NOTIFY is not described in the property.
+ */
+ public String getNotifyMethodName();
+
+ /**
+ * Return the value of REVISION or null if REVISION is not described in the property.
+ * The return type is Long in order to accommodate unsigned C++ 32-bit values.
+ */
+ public Long getRevision();
+
+ /**
+ * Return the value of DESIGNABLE or null if DESIGNABLE is not described in the property.
+ */
+ public String getDesignable();
+
+ /**
+ * Return the value of SCRIPTABLE or null if SCRIPTABLE is not described in the property.
+ */
+ public String getScriptable();
+
+ /**
+ * Return the value of STORED or null if STORED is not described in the property.
+ */
+ public String getStored();
+
+ /**
+ * Return the value of USER or null if USER is not described in the property.
+ */
+ public String getUser();
+
+ /**
+ * Return true if CONSTANT was specified in the Q_PROPERTY expansion and false otherwise.
+ */
+ public boolean isConstant();
+
+ /**
+ * Return true if FINAL was specified in the Q_PROPERTY expansion and false otherwise.
+ */
+ public boolean isFinal();
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/AbstractQField.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/AbstractQField.java
new file mode 100644
index 0000000..9d11ebb
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/AbstractQField.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2013 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.qt.internal.core.index;
+
+import org.eclipse.cdt.qt.core.index.IQObject;
+import org.eclipse.cdt.qt.core.index.IQObject.IMember;
+
+public abstract class AbstractQField implements IQObject.IMember {
+
+ private final IQObject owner;
+ protected String name;
+
+ /**
+ * Scan the given field and extracts the strings defining the attributes of the
+ * field. Returns false if the expansion parameter, does not represent a Q_PROPERTY,
+ * does not have related information, or if the information does not match the
+ * expected format.
+ */
+ protected abstract boolean scanDefn(String expansionParam);
+
+ protected AbstractQField(IQObject owner) {
+ this.owner = owner;
+ }
+
+ @Override
+ public IQObject getOwner() {
+ return owner;
+ }
+
+ @Override
+ public boolean isOverride(IMember member) {
+ if (!AbstractQField.class.isAssignableFrom(member.getClass()))
+ return false;
+
+ // I haven't been able to find Qt documentation describing how Q_PROPERY is overridden,
+ // but the docs suggest it is just by name.
+
+ AbstractQField other = (AbstractQField) member;
+ return name == null ? other.name == null : name.equals(other.name);
+ }
+}
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 be86d8a..9a050d5 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
@@ -13,9 +13,10 @@ import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.dom.ast.IBinding;
-import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.qt.core.index.IQEnum;
import org.eclipse.cdt.qt.core.index.IQObject;
+import org.eclipse.cdt.qt.core.index.IQProperty;
+import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMProperty;
import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQEnum;
import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQObject;
import org.eclipse.core.runtime.CoreException;
@@ -25,6 +26,7 @@ public class QObject implements IQObject {
private final String name;
private final QtPDOMQObject pdomQObject;
private final List<IQObject> bases;
+ private final IQObject.IMembers<IQProperty> properties;
private final List<IQEnum> enums;
private final Map<String, String> classInfos;
@@ -32,18 +34,29 @@ public class QObject implements IQObject {
this.name = pdomQObject.getName();
this.pdomQObject = pdomQObject;
+ List<IQProperty> baseProps = new ArrayList<IQProperty>();
+
this.bases = new ArrayList<IQObject>();
- for(QtPDOMQObject base : pdomQObject.findBases())
- this.bases.add(new QObject(qtIndex, cdtIndex, base));
+ for(QtPDOMQObject base : pdomQObject.findBases()) {
+ QObject baseQObj = new QObject(qtIndex, cdtIndex, base);
+ this.bases.add(baseQObj);
+ baseProps.addAll(baseQObj.getProperties().all());
+ }
this.classInfos = pdomQObject.getClassInfos();
this.enums = new ArrayList<IQEnum>();
- for(IField field : pdomQObject.getFields())
- if (field instanceof QtPDOMQEnum) {
- QtPDOMQEnum qEnum = (QtPDOMQEnum) field;
- this.enums.add(new QEnum(field.getName(), qEnum.isFlag(), qEnum.getEnumerators()));
- }
+ for(QtPDOMQEnum pdom : pdomQObject.getFields(QtPDOMQEnum.class))
+ this.enums.add(new QEnum(pdom.getName(), pdom.isFlag(), pdom.getEnumerators()));
+
+ List<IQProperty> props = new ArrayList<IQProperty>();
+ for(QtPDOMProperty pdom : pdomQObject.getFields(QtPDOMProperty.class)) {
+ QProperty qProp = new QProperty(this, pdom.getTypeStr(), pdom.getName());
+ for(QtPDOMProperty.Attribute attr : pdom.getAttributes())
+ qProp.setAttribute(attr.attr, attr.value);
+ props.add(qProp);
+ }
+ this.properties = QObjectMembers.create(props, baseProps);
}
@Override
@@ -62,6 +75,11 @@ public class QObject implements IQObject {
}
@Override
+ public IQObject.IMembers<IQProperty> getProperties() {
+ return properties;
+ }
+
+ @Override
public String getClassInfo(String key) {
String value = classInfos.get(key);
if (value != null)
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObjectMembers.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObjectMembers.java
new file mode 100644
index 0000000..73116d9
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QObjectMembers.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.qt.internal.core.index;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.cdt.qt.core.index.IQObject;
+
+public class QObjectMembers<T extends IQObject.IMember> implements IQObject.IMembers<T> {
+
+ private final List<T> all;
+ private final Collection<T> locals;
+ private Collection<T> withoutOverrides;
+
+ public static <T extends IQObject.IMember> QObjectMembers<T> create(Collection<T> locals, Collection<T> inherited) {
+ // NOTE: All must be ordered with the locals before the inherited members. This ensures that
+ // the algorithm for computing #withoutOverrides will filter out the parent members and
+ // not the local ones.
+ // @see withoutOverrides()
+ ArrayList<T> all = new ArrayList<T>(locals.size() + inherited.size());
+ all.addAll(locals);
+ all.addAll(inherited);
+ return new QObjectMembers<T>(all, locals);
+ }
+
+ private QObjectMembers(List<T> all, Collection<T> locals) {
+ this.all = Collections.unmodifiableList(all);
+ this.locals = Collections.unmodifiableCollection(locals);
+ }
+
+ @Override
+ public Collection<T> all() {
+ return all;
+ }
+
+ @Override
+ public Collection<T> locals() {
+ return locals;
+ }
+
+ @Override
+ public Collection<T> withoutOverrides() {
+
+ if (withoutOverrides == null)
+ synchronized (all) {
+ if (withoutOverrides == null) {
+
+ // Naively tests each existing element for override before inserting the new
+ // element. Most member lists have less than 3 elements, and the largest that
+ // I've found (in the Qt impl) is about 20; so performance may not be as bad
+ // as it seems.
+ //
+ // An earlier approach tried to use a SortedSet with the #isOverride result in
+ // the Comparator. The problem with the approach is finding a stable sort order
+ // when the members don't override each other.
+ // E.g., if o1 and o2 override each other and m is unrelated, we could get a
+ // tree like:
+ // m
+ // / \
+ // o1 o2
+
+ ArrayList<T> filtered = new ArrayList<T>(all.size());
+ for(T member : all) {
+ boolean isOverridden = false;
+ for(Iterator<T> i = filtered.iterator(); !isOverridden && i.hasNext(); )
+ isOverridden = member.isOverride(i.next());
+ if (!isOverridden)
+ filtered.add(member);
+ }
+
+ withoutOverrides = Collections.unmodifiableCollection(filtered);
+ }
+ }
+
+ return withoutOverrides;
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QProperty.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QProperty.java
new file mode 100644
index 0000000..8d0bf81
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/index/QProperty.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2013 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.qt.internal.core.index;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.cdt.qt.core.index.IQObject;
+import org.eclipse.cdt.qt.core.index.IQProperty;
+
+public class QProperty extends AbstractQField implements IQProperty {
+
+ private String type;
+ private final String[] values = new String[Attribute.values().length];
+
+ public QProperty(IQObject owner, String type, String name) {
+ super(owner);
+ this.type = type;
+ this.name = name;
+ }
+
+ /**
+ * A regular expression for scanning the full Q_PROPERTY expansion and extracting the
+ * expansion parameter. It provides the following capture groups:
+ * 1 - the type
+ * 2 - the name
+ * 3 - the trimmed remainder of the expansion parameter (starting with READ)
+ *
+ * This REGEX handles cases like:
+ * Q_PROPERTY(T* t READ ... )
+ * Q_PROPERTY(T * t READ ... )
+ * Q_PROPERTY(T *t READ ... )
+ * This REGEX assumes that READ will directly follow the property name. This is implied,
+ * although not explicitly stated in the Qt documentation.
+ *
+ * It also allows the option of having no other attribute (just type and name). The Qt
+ * documentation forbids this, but it is used in QtSensors/
+ */
+ private static final Pattern EXPANSION_REGEX = Pattern.compile("^(.+?)\\s*([a-zA-Z_][\\w]*+)(?:(?:\\s+(READ\\s+.*))|\\s*)$");
+
+ /**
+ * A regular expression for scanning Q_PROPERTY attributes. The regular expression is built
+ * from the values defined in IQProperty#Attribute. It looks like:
+ * <pre>
+ * (:?READ)|(?:WRITE)|(:?RESET)|...
+ * </pre>
+ * This regular expression is used to recognize valid attributes while scanning the
+ * Q_PROPERTY macro expansion.
+ *
+ * @see QProperty#scanAttributes(String)
+ */
+ private static final Pattern ATTRIBUTE_REGEX;
+ static {
+ StringBuilder regexBuilder = new StringBuilder();
+ for(IQProperty.Attribute attr : IQProperty.Attribute.values()) {
+ if (attr.ordinal() > 0)
+ regexBuilder.append('|');
+ regexBuilder.append("(:?");
+ regexBuilder.append(attr.identifier);
+ regexBuilder.append(")");
+ }
+ ATTRIBUTE_REGEX = Pattern.compile(regexBuilder.toString());
+ }
+
+ /**
+ * Scans the given field and extracts the strings defining the attributes of the
+ * Q_PROPERTY. Returns false if the field is does not represent a Q_PROPERTY, does
+ * not have attribute-related information, or if the information does not match the
+ * expected format.
+ * @param field
+ * @return
+ */
+ @Override
+ protected boolean scanDefn(String expansionParam) {
+ Matcher m = EXPANSION_REGEX.matcher(expansionParam);
+ if (!m.matches())
+ return false;
+
+ this.type = m.group(1);
+ this.name = m.group(2);
+ return scanAttributes(m.group(3));
+ }
+
+ /**
+ * Scans the given string to extract values for all recognized attributes. A regular expression
+ * is used to find the attributes, substrings between attributes are assigned as values.
+ * Attributes that don't expect a value (as determined by {@link IQProperty#Attribute#hasValue}),
+ * as assigned "true".
+ */
+ private boolean scanAttributes(String attributes) {
+ if (attributes == null)
+ return true;
+
+ int lastEnd = 0;
+ IQProperty.Attribute lastAttr = null;
+ for(Matcher attributeMatcher = ATTRIBUTE_REGEX.matcher(attributes); attributeMatcher.find(); lastEnd = attributeMatcher.end()) {
+ // set the value of attribute found in the previous iteration to the substring between
+ // the end of that attribute and the start of this one
+ if (lastAttr != null) {
+ String value = attributes.substring(lastEnd, attributeMatcher.start());
+ values[lastAttr.ordinal()] = value.trim();
+ }
+
+ // the regex is built from the definition of the enum, so none of the strings that it
+ // finds will throw an exception
+ lastAttr = IQProperty.Attribute.valueOf(IQProperty.Attribute.class, attributeMatcher.group(0));
+
+ // if this attribute doesn't have a value, then put it into the value map immediately
+ // and make sure it is not used later in this scan
+ if (!lastAttr.hasValue) {
+ values[lastAttr.ordinal()] = Boolean.TRUE.toString();
+ lastAttr = null;
+ }
+ }
+
+ // the value of the last attribute in the expansion is the substring between the end of
+ // the attribute identifier and the end of the string
+ if (lastAttr != null) {
+ String value = attributes.substring(lastEnd);
+ values[lastAttr.ordinal()] = value.trim();
+ }
+
+ return true;
+ }
+
+ public void setAttribute(IQProperty.Attribute attr, String value) {
+ values[attr.ordinal()] = ( value == null ? "" : value );
+ }
+
+ @Override
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getValue(Attribute attr) {
+ return values[attr.ordinal()];
+ }
+
+ @Override
+ public String getReadMethodName() {
+ return Attribute.READ.valueIn(this);
+ }
+
+ @Override
+ public String getWriteMethodName() {
+ return Attribute.WRITE.valueIn(this);
+ }
+
+ @Override
+ public String getResetMethodName() {
+ return Attribute.RESET.valueIn(this);
+ }
+
+ @Override
+ public String getNotifyMethodName() {
+ return Attribute.NOTIFY.valueIn(this);
+ }
+
+ @Override
+ public Long getRevision() {
+ String revision = Attribute.REVISION.valueIn(this);
+ if (revision != null)
+ try {
+ return Long.valueOf(revision);
+ } catch(NumberFormatException e) {
+ // This is a problem with the user's C++ code, there is no need to log this exception,
+ // just ignore the value.
+ }
+
+ return null;
+ }
+
+ @Override
+ public String getDesignable() {
+ return Attribute.DESIGNABLE.valueIn(this);
+ }
+
+ @Override
+ public String getScriptable() {
+ return Attribute.SCRIPTABLE.valueIn(this);
+ }
+
+ @Override
+ public String getStored() {
+ return Attribute.STORED.valueIn(this);
+ }
+
+ @Override
+ public String getUser() {
+ return Attribute.USER.valueIn(this);
+ }
+
+ @Override
+ public boolean isConstant() {
+ return Attribute.CONSTANT.valueIn(this) != null;
+ }
+
+ @Override
+ public boolean isFinal() {
+ return Attribute.FINAL.valueIn(this) != null;
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTDelegatedName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTDelegatedName.java
index d102a46..81b66da 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTDelegatedName.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTDelegatedName.java
@@ -15,7 +15,6 @@ import org.eclipse.cdt.core.dom.ast.IASTCompletionContext;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTImageLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
-import org.eclipse.cdt.core.dom.ast.IASTNameOwner;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNodeLocation;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
@@ -35,53 +34,6 @@ public abstract class ASTDelegatedName implements IASTName {
protected IBinding binding;
- /**
- * Some Qt elements are introduced with empty macro expansions. The Qt linkage handles this
- * by creating a new name and then adding it as a reference to the C++ language element.
- * This utility helps by containing that C++ name and the location of the Qt name.
- */
- public static class Reference extends ASTDelegatedName {
-
- private final IASTFileLocation location;
-
- public Reference(IASTName name, IASTFileLocation location) {
- super(name);
- this.location = location;
- }
-
- @Override
- protected IBinding createBinding() {
- return delegate.resolveBinding();
- }
-
- @Override
- public IASTFileLocation getFileLocation() {
- return location;
- }
-
- @Override
- public boolean isReference() {
- return true;
- }
-
- @Override
- public boolean isDefinition() {
- return false;
- }
-
- @Override
- public boolean isDeclaration() {
- return false;
- }
-
- @Override
- public int getRoleOfName(boolean allowResolution) {
- return IASTNameOwner.r_reference;
- }
- }
-
- protected abstract IBinding createBinding();
-
protected ASTDelegatedName(IASTName delegate) {
this.delegate = delegate;
}
@@ -208,14 +160,12 @@ public abstract class ASTDelegatedName implements IASTName {
@Override
public IBinding getBinding() {
- return binding;
+ return null;
}
@Override
public IBinding resolveBinding() {
- if (binding == null)
- binding = createBinding();
- return binding;
+ return null;
}
@Override
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTNameReference.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTNameReference.java
new file mode 100644
index 0000000..3ab9467
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/ASTNameReference.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2013 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.qt.internal.core.pdom;
+
+import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
+import org.eclipse.cdt.core.dom.ast.IASTName;
+import org.eclipse.cdt.core.dom.ast.IASTNameOwner;
+import org.eclipse.cdt.core.dom.ast.IBinding;
+import org.eclipse.cdt.core.index.IIndexName;
+import org.eclipse.cdt.internal.core.index.IIndexFragmentBinding;
+import org.eclipse.cdt.internal.core.index.IIndexFragmentName;
+import org.eclipse.cdt.internal.core.pdom.dom.IPDOMIterator;
+import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
+import org.eclipse.cdt.internal.core.pdom.dom.PDOMName;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * Some Qt elements are introduced with empty macro expansions. The Qt linkage handles this
+ * by creating a new name and then adding it as a reference to the C++ language element.
+ * This utility helps by containing that C++ name and the location of the Qt name.
+ */
+@SuppressWarnings("restriction")
+public class ASTNameReference extends ASTDelegatedName {
+
+ private final IASTFileLocation location;
+
+ /**
+ * Create and return a name that will reference the given name.
+ */
+ public ASTNameReference(IASTName name) {
+ super(name);
+ this.location = name.getFileLocation();
+ }
+
+ /**
+ * Create and return a name that will reference the given name from the given location.
+ */
+ public ASTNameReference(IASTName name, IASTFileLocation location) {
+ super(name);
+ this.location = location;
+ }
+
+ /**
+ * Find and return the Qt binding that annotates the given PDOMBinding. E.g., if the input binding
+ * is an instance of PDOMCPPClassType, then this method will return the QtPDOMQObject that was created
+ * from that class (or null if there is no such Qt element).
+ * <p>
+ * This is implemented by creating an ASTNameReference within the Qt element binding's definition. That
+ * name is added as reference from the C++ PDOM binding.
+ */
+ public static <T extends QtPDOMBinding> T findFromBinding(Class<T> cls, PDOMBinding binding) throws CoreException {
+ if (binding == null)
+ return null;
+
+ // Look for external references to the binding.
+ IPDOMIterator<PDOMName> pdomIterator = binding.getExternalReferences();
+ while(pdomIterator.hasNext()) {
+ PDOMName extRef = pdomIterator.next();
+ IIndexName caller = extRef.getEnclosingDefinition();
+ if (caller instanceof IIndexFragmentName) {
+ IIndexFragmentBinding extRefBinding = ((IIndexFragmentName) caller).getBinding();
+ if (cls.isAssignableFrom(extRefBinding.getClass()))
+ return cls.cast(extRefBinding);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public IBinding resolveBinding() {
+ return delegate.resolveBinding();
+ }
+
+ @Override
+ public IBinding getBinding() {
+ return delegate.getBinding();
+ }
+
+ @Override
+ public IASTFileLocation getFileLocation() {
+ return location;
+ }
+
+ @Override
+ public boolean isReference() {
+ return true;
+ }
+
+ @Override
+ public boolean isDefinition() {
+ return false;
+ }
+
+ @Override
+ public boolean isDeclaration() {
+ return false;
+ }
+
+ @Override
+ public int getRoleOfName(boolean allowResolution) {
+ return IASTNameOwner.r_reference;
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/AbstractQObjectFieldName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/AbstractQObjectFieldName.java
new file mode 100644
index 0000000..a9bf082
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/AbstractQObjectFieldName.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2013 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.qt.internal.core.pdom;
+
+import org.eclipse.cdt.core.dom.ILinkage;
+import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
+import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
+import org.eclipse.cdt.core.dom.ast.IASTImageLocation;
+import org.eclipse.cdt.core.dom.ast.IASTName;
+import org.eclipse.cdt.core.dom.ast.IASTNameOwner;
+import org.eclipse.cdt.core.dom.ast.IASTNode;
+import org.eclipse.cdt.internal.core.dom.Linkage;
+import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
+import org.eclipse.core.runtime.CoreException;
+
+@SuppressWarnings("restriction")
+public abstract class AbstractQObjectFieldName extends ASTDelegatedName {
+
+ private final QObjectName owner;
+ private final String name;
+ private final QtASTImageLocation location;
+ private ASTNodeProperty propertyInParent;
+
+ protected AbstractQObjectFieldName(QObjectName owner, IASTName ast, String name, QtASTImageLocation location) {
+ super(ast);
+ this.owner = owner;
+ this.name = name;
+ this.location = location;
+ }
+
+ protected PDOMBinding getOwner(QtPDOMLinkage linkage) throws CoreException {
+ return linkage.getBinding(owner);
+ }
+
+ public String getFieldName() {
+ return name;
+ }
+
+ @Override
+ public IASTFileLocation getFileLocation() {
+ return location;
+ }
+
+ @Override
+ public IASTNode getParent() {
+ return owner;
+ }
+
+ @Override
+ public IASTNode[] getChildren() {
+ return IASTNode.EMPTY_NODE_ARRAY;
+ }
+
+ @Override
+ public void setParent(IASTNode node) {
+ throw new IllegalStateException("attempt to modify parent of QObject field"); //$NON-NLS-1$
+ }
+
+ @Override
+ public ASTNodeProperty getPropertyInParent() {
+ return propertyInParent;
+ }
+
+ @Override
+ public void setPropertyInParent(ASTNodeProperty property) {
+ propertyInParent = property;
+ }
+
+ @Override
+ public char[] getSimpleID() {
+ return name.toCharArray();
+ }
+
+ @Override
+ public String getRawSignature() {
+ return name;
+ }
+
+ @Override
+ public boolean isDeclaration() {
+ return false;
+ }
+
+ @Override
+ public boolean isReference() {
+ return false;
+ }
+
+ @Override
+ public boolean isDefinition() {
+ return true;
+ }
+
+ @Override
+ public int getRoleOfName(boolean allowResolution) {
+ return IASTNameOwner.r_definition;
+ }
+
+ @Override
+ public ILinkage getLinkage() {
+ return Linkage.QT_LINKAGE;
+ }
+
+ @Override
+ public IASTImageLocation getImageLocation() {
+ return location;
+ }
+
+ @Override
+ public IASTName copy() {
+ return copy(CopyStyle.withoutLocations);
+ }
+
+ @Override
+ public IASTName copy(CopyStyle style) {
+ throw new UnsupportedOperationException("attempt to copy QObject field"); //$NON-NLS-1$
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/IQtASTName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/IQtASTName.java
new file mode 100644
index 0000000..aead142
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/IQtASTName.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2013 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.qt.internal.core.pdom;
+
+import org.eclipse.cdt.core.dom.ast.IASTName;
+import org.eclipse.core.runtime.CoreException;
+
+public interface IQtASTName extends IASTName {
+ /**
+ * Create and return a new instance of PDOMBinding for the receiver. The implementation
+ * is allowed to return null if there is no possibility of creating a PDOMBinding.
+ * The value that is returned must be consistent -- if null is returned one time then
+ * it must be returned every time.
+ */
+ public QtPDOMBinding createPDOMBinding(QtPDOMLinkage linkage) throws CoreException;
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/IQtPDOMCodec.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/IQtPDOMCodec.java
new file mode 100644
index 0000000..040f2a8
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/IQtPDOMCodec.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.qt.internal.core.pdom;
+
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * A utility interface for encoding and decoding fixed-sized elements to and
+ * from the Database.
+ */
+public interface IQtPDOMCodec<T> {
+ /**
+ * Return the number of bytes needed to store a single element.
+ */
+ public int getElementSize();
+
+ /**
+ * Allocate and return a new array to hold the specified number of elements.
+ */
+ public T[] allocArray(int count);
+
+ /**
+ * Examine the database at the specified record to decode an element instance.
+ */
+ public T decode(QtPDOMLinkage linkage, long record) throws CoreException;
+
+ /**
+ * Encode the given element into the database at the specified record. The codec is
+ * responsible for releasing storage that is about to be overwritten (if needed).
+ * The element will be null when the implementation should delete all memory used
+ * for storage at record.
+ */
+ public void encode(QtPDOMLinkage linkage, long record, T element) throws CoreException;
+}
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 9699c79..d84e11b 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,7 +7,9 @@
*/
package org.eclipse.cdt.qt.internal.core.pdom;
+import java.util.ArrayList;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.dom.ILinkage;
@@ -16,18 +18,19 @@ import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
-import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.internal.core.dom.Linkage;
+import org.eclipse.core.runtime.CoreException;
/**
* QObjects are C++ classes that have been annotated with Qt marker macros. This class is
* used to introduce the QObject to the Qt linkage.
*/
@SuppressWarnings("restriction")
-public class QObjectName extends ASTDelegatedName {
+public class QObjectName extends ASTDelegatedName implements IQtASTName {
private final ICPPASTCompositeTypeSpecifier spec;
+ private final List<QtPropertyName> properties = new ArrayList<QtPropertyName>();
private final Map<String, String> classInfos = new LinkedHashMap<String, String>();
private IASTNode parent;
@@ -40,6 +43,14 @@ public class QObjectName extends ASTDelegatedName {
this.propertyInParent = delegate.getPropertyInParent();
}
+ public List<QtPropertyName> getProperties() {
+ return properties;
+ }
+
+ public void addProperty(QtPropertyName property) {
+ properties.add(property);
+ }
+
public Map<String, String> getClassInfos() {
return classInfos;
}
@@ -49,8 +60,8 @@ public class QObjectName extends ASTDelegatedName {
}
@Override
- protected IBinding createBinding() {
- return new QtBinding(QtPDOMNodeType.QObject, this, spec.getName());
+ public QtPDOMBinding createPDOMBinding(QtPDOMLinkage linkage) throws CoreException {
+ return new QtPDOMQObject(linkage, this, spec.getName());
}
@Override
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 b8a3b6d..6b5256e 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
@@ -15,7 +15,9 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
+import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
+import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
@@ -23,12 +25,15 @@ import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion;
import org.eclipse.cdt.core.dom.ast.IBinding;
+import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.index.IIndexSymbols;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.parser.scanner.LocationMap;
import org.eclipse.cdt.qt.core.QtKeywords;
+import org.eclipse.cdt.qt.core.index.IQProperty;
+import org.eclipse.cdt.qt.internal.core.index.QProperty;
@SuppressWarnings("restriction")
public class QtASTVisitor extends ASTVisitor {
@@ -36,10 +41,10 @@ public class QtASTVisitor extends ASTVisitor {
private final IIndexSymbols symbols;
private final LocationMap locationMap;
- private static final Pattern expansionParamRegex = Pattern.compile("^" + "(?:Q_ENUMS|Q_FLAGS)" + "\\s*\\((.*)\\)$");
+ private static final Pattern expansionParamRegex = Pattern.compile("^(?:Q_ENUMS|Q_FLAGS)\\s*\\((.*)\\)$", Pattern.DOTALL);
private static final Pattern qualNameRegex = Pattern.compile("\\s*((?:[^\\s:]+\\s*::\\s*)*[^\\s:]+).*");
- 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*\\)$", Pattern.DOTALL);
/**
* A regular expression for scanning the Q_CLASSINFO expansion and extracting the
@@ -49,7 +54,35 @@ public class QtASTVisitor extends ASTVisitor {
* <p>
* The key must not have embedded quotes.
*/
- private static final Pattern classInfoRegex = Pattern.compile("^Q_CLASSINFO\\s*\\(\\s*\"([^\"]+)\"\\s*,\\s*\"(.*)\"\\s*\\)$");
+ private static final Pattern classInfoRegex = Pattern.compile("^Q_CLASSINFO\\s*\\(\\s*\"([^\"]+)\"\\s*,\\s*\"(.*)\"\\s*\\)$", Pattern.DOTALL);
+
+ private static final Pattern leadingWhitespaceRegex = Pattern.compile("^\\s*([^\\s].*)$");
+
+ private static final Pattern qPropertyRegex = Pattern.compile("^Q_PROPERTY\\s*\\(\\s*(.+?)\\s*([a-zA-Z_][\\w]*+)(?:(?:\\s+(READ\\s+.*))|\\s*)\\s*\\)$", Pattern.DOTALL);
+
+ /**
+ * A regular expression for scanning Q_PROPERTY attributes. The regular expression is built
+ * from the values defined in IQProperty#Attribute. It looks like:
+ * <pre>
+ * (:?READ)|(?:WRITE)|(:?RESET)|...
+ * </pre>
+ * This regular expression is used to recognize valid attributes while scanning the
+ * Q_PROPERTY macro expansion.
+ *
+ * @see QProperty#scanAttributes(String)
+ */
+ private static final Pattern qPropertyAttributeRegex;
+ static {
+ StringBuilder regexBuilder = new StringBuilder();
+ for(IQProperty.Attribute attr : IQProperty.Attribute.values()) {
+ if (attr.ordinal() > 0)
+ regexBuilder.append('|');
+ regexBuilder.append("(:?");
+ regexBuilder.append(attr.identifier);
+ regexBuilder.append(")");
+ }
+ qPropertyAttributeRegex = Pattern.compile(regexBuilder.toString());
+ }
public QtASTVisitor(IIndexSymbols symbols, LocationMap locationMap) {
shouldVisitDeclSpecifiers = true;
@@ -117,7 +150,7 @@ public class QtASTVisitor extends ASTVisitor {
symbols.add(owner, astName, qobjName);
if (cppName != null)
- symbols.add(owner, new ASTDelegatedName.Reference(cppName, location), astName);
+ symbols.add(owner, new ASTNameReference(cppName, location), astName);
}
}
}
@@ -128,9 +161,12 @@ public class QtASTVisitor extends ASTVisitor {
QObjectName qobjName = new QObjectName(spec);
symbols.add(owner, qobjName, null);
+ // The QObject contains a reference to the C++ class that it annotates.
+ symbols.add(owner, new ASTNameReference(spec.getName()), qobjName);
+
// There are three macros that are significant to QEnums, Q_ENUMS, Q_FLAGS, and Q_DECLARE_FLAGS.
// All macro expansions in the QObject class definition are examined to find instances of these
- // three. Two lists are created during this processing. Then the those lists are uses to create
+ // three. Two lists are created during this processing. Then those lists are uses to create
// the QEnum instances.
List<EnumDecl> enumDecls = new ArrayList<QtASTVisitor.EnumDecl>();
@@ -140,6 +176,7 @@ public class QtASTVisitor extends ASTVisitor {
String macroName = String.valueOf(expansion.getMacroReference());
if (QtKeywords.Q_OBJECT.equals(macroName))
continue;
+
if (QtKeywords.Q_ENUMS.equals(macroName))
extractEnumDecls(expansion, false, enumDecls);
else if (QtKeywords.Q_FLAGS.equals(macroName))
@@ -158,7 +195,8 @@ public class QtASTVisitor extends ASTVisitor {
String value = m.group(2);
qobjName.addClassInfo(key, value);
}
- }
+ } else if(QtKeywords.Q_PROPERTY.equals(macroName))
+ handleQPropertyDefn(owner, qobjName, expansion);
}
for(EnumDecl decl : enumDecls)
@@ -185,4 +223,167 @@ public class QtASTVisitor extends ASTVisitor {
decls.add(new EnumDecl(enumName, isFlag, refName, offset + start, end - start));
}
}
+
+ private void handleQPropertyDefn(IASTPreprocessorIncludeStatement owner, QObjectName qobjName, IASTPreprocessorMacroExpansion expansion) {
+ Matcher m = qPropertyRegex.matcher(expansion.getRawSignature());
+ if (!m.matches())
+ return;
+
+ String type = m.group(1);
+ String name = m.group(2);
+
+ int nameStart = m.start(2);
+ int nameEnd = m.end(2);
+
+ IASTName refName = expansion.getMacroReference();
+ QtASTImageLocation location = new QtASTImageLocation(refName.getFileLocation(), nameStart, nameEnd - nameStart);
+
+ QtPropertyName propertyName = new QtPropertyName(qobjName, refName, name, location);
+ propertyName.setType(type);
+ qobjName.addProperty(propertyName);
+ symbols.add(owner, propertyName, qobjName);
+
+ // Create nodes for all the attributes.
+ AttrValue[] values = new AttrValue[IQProperty.Attribute.values().length];
+ String attributes = m.group(3);
+ if (attributes == null)
+ return;
+
+ int attrOffset = m.start(3);
+
+ int lastEnd = 0;
+ IQProperty.Attribute lastAttr = null;
+ for(Matcher attributeMatcher = qPropertyAttributeRegex.matcher(attributes); attributeMatcher.find(); lastEnd = attributeMatcher.end()) {
+ // set the value of attribute found in the previous iteration to the substring between
+ // the end of that attribute and the start of this one
+ if (lastAttr != null) {
+ String value = attributes.substring(lastEnd, attributeMatcher.start());
+ int wsOffset = 0;
+ Matcher ws = leadingWhitespaceRegex.matcher(value);
+ if (ws.matches()) {
+ value = ws.group(1);
+ wsOffset = ws.start(1);
+ }
+
+ values[lastAttr.ordinal()] = new AttrValue(attrOffset + lastEnd + wsOffset, value.trim());
+ }
+
+ // the regex is built from the definition of the enum, so none of the strings that it
+ // finds will throw an exception
+ lastAttr = IQProperty.Attribute.valueOf(IQProperty.Attribute.class, attributeMatcher.group(0));
+
+ // if this attribute doesn't have a value, then put it into the value map immediately
+ // and make sure it is not used later in this scan
+ if (!lastAttr.hasValue) {
+ values[lastAttr.ordinal()] = AttrValue.None;
+ lastAttr = null;
+ }
+ }
+
+ // the value of the last attribute in the expansion is the substring between the end of
+ // the attribute identifier and the end of the string
+ if (lastAttr != null) {
+ String value = attributes.substring(lastEnd);
+ int wsOffset = 0;
+ Matcher ws = leadingWhitespaceRegex.matcher(value);
+ if (ws.matches()) {
+ value = ws.group(1);
+ wsOffset = ws.start(1);
+ }
+
+ values[lastAttr.ordinal()] = new AttrValue(attrOffset + lastEnd + wsOffset, value.trim());
+ }
+
+ // Put all values into the property name.
+ for(int i = 0; i < values.length; ++i) {
+ IQProperty.Attribute attr = IQProperty.Attribute.values()[i];
+ AttrValue value = values[i];
+ if (value == null)
+ continue;
+
+ // If the attribute is not expected to have a C++ binding as the value, then it can
+ // be immediately added to the Q_PROPERTY.
+ if (!couldHaveBinding(attr)) {
+ propertyName.addAttribute(attr, value.value);
+ continue;
+ }
+
+ // Otherwise see if one or more bindings can be found for the value of the attribute.
+ // TODO Check whether the Qt moc allows for inherited methods.
+ IBinding[] bindings = null;
+ IASTNode specNode = qobjName.getParent();
+ if (specNode instanceof IASTCompositeTypeSpecifier) {
+ IScope scope = ((IASTCompositeTypeSpecifier) specNode).getScope();
+ bindings = CPPSemantics.findBindings(scope, value.value, false);
+ }
+
+ // If no bindings are found, then the attribute can be immediately added to the Q_PROPERTY.
+ if (bindings == null || bindings.length <= 0) {
+ propertyName.addAttribute(attr, value.value);
+ continue;
+ }
+
+ // Otherwise create a new attribute for each binding.
+ for(IBinding foundBinding : bindings) {
+ propertyName.addAttribute(attr, value.value, foundBinding);
+
+ IASTName cppName = findASTName(foundBinding);
+ if (cppName != null) {
+ QtASTImageLocation attrLoc = new QtASTImageLocation(refName.getFileLocation(), value.offset, value.value.length());
+ symbols.add(owner, new ASTNameReference(cppName, attrLoc), propertyName);
+ }
+ }
+ }
+ }
+
+ private static boolean couldHaveBinding(IQProperty.Attribute attr) {
+ switch(attr) {
+ case READ:
+ case WRITE:
+ case RESET:
+ case NOTIFY:
+ case DESIGNABLE:
+ case SCRIPTABLE:
+ return true;
+
+ case REVISION:
+ case STORED:
+ case USER:
+ case CONSTANT:
+ case FINAL:
+ default:
+ return false;
+ }
+ }
+
+ private static IASTName findASTName(IBinding binding) {
+ IASTNode node = null;
+ if (binding instanceof ICPPInternalBinding) {
+ node = ((ICPPInternalBinding) binding).getDefinition();
+ if (node == null)
+ node = ((ICPPInternalBinding) binding).getDeclarations()[0];
+ }
+
+ if (node == null)
+ return null;
+
+ IASTName astName = node instanceof IASTName ? (IASTName) node : null;
+ if (astName != null)
+ return astName;
+
+ if (node instanceof IASTDeclarator)
+ return ((IASTDeclarator) node).getName();
+
+ return null;
+ }
+
+ private static class AttrValue {
+ public final int offset;
+ public final String value;
+ public AttrValue(int offset, String value) {
+ this.offset = offset;
+ this.value = value;
+ }
+ public static final AttrValue None = new AttrValue(0, null);
+ }
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtBinding.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtBinding.java
deleted file mode 100644
index 1537f83..0000000
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtBinding.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (c) 2013 QNX Software Systems and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- */
-package org.eclipse.cdt.qt.internal.core.pdom;
-
-import org.eclipse.cdt.core.dom.ILinkage;
-import org.eclipse.cdt.core.dom.ast.DOMException;
-import org.eclipse.cdt.core.dom.ast.IASTName;
-import org.eclipse.cdt.core.dom.ast.IBinding;
-import org.eclipse.cdt.core.dom.ast.IScope;
-import org.eclipse.cdt.internal.core.dom.Linkage;
-
-@SuppressWarnings("restriction")
-public class QtBinding implements IBinding {
-
- private final QtPDOMNodeType type;
- private final QtBinding owner;
- private final IASTName qtName;
- private final IASTName cppName;
-
- private QtPDOMBinding pdomBinding;
-
- public QtBinding(QtPDOMNodeType type, IASTName qtName, IASTName cppName) {
- this(type, null, qtName, cppName);
- }
-
- public QtBinding(QtPDOMNodeType type, QtBinding owner, IASTName qtName, IASTName cppName) {
- this.type = type;
- this.owner = owner;
- this.qtName = qtName;
- this.cppName = cppName;
- }
-
- public QtPDOMNodeType getType() {
- return type;
- }
-
- public IASTName getQtName() {
- return qtName;
- }
-
- public IASTName getCppName() {
- return cppName;
- }
-
- public void setPDOMBinding(QtPDOMBinding pdomBinding) {
- this.pdomBinding = pdomBinding;
- }
-
- @Override
- @SuppressWarnings({ "rawtypes" })
- public Object getAdapter(Class adapter) {
- if (getClass().isAssignableFrom(adapter))
- return this;
- if (QtPDOMBinding.class.isAssignableFrom(adapter))
- return pdomBinding;
-
- return null;
- }
-
- @Override
- public String getName() {
- return String.valueOf(getNameCharArray());
- }
-
- @Override
- public char[] getNameCharArray() {
- return qtName.getSimpleID();
- }
-
- @Override
- public ILinkage getLinkage() {
- return Linkage.QT_LINKAGE;
- }
-
- @Override
- public IBinding getOwner() {
- return owner;
- }
-
- @Override
- public IScope getScope() throws DOMException {
- return null;
- }
-}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtEnumName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtEnumName.java
index 5536f11..fa766ff 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtEnumName.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtEnumName.java
@@ -7,33 +7,17 @@
*/
package org.eclipse.cdt.qt.internal.core.pdom;
-import org.eclipse.cdt.core.dom.ILinkage;
-import org.eclipse.cdt.core.dom.ast.ASTNodeProperty;
-import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
-import org.eclipse.cdt.core.dom.ast.IASTImageLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
-import org.eclipse.cdt.core.dom.ast.IASTNameOwner;
-import org.eclipse.cdt.core.dom.ast.IASTNode;
-import org.eclipse.cdt.core.dom.ast.IBinding;
-import org.eclipse.cdt.internal.core.dom.Linkage;
+import org.eclipse.core.runtime.CoreException;
-@SuppressWarnings("restriction")
-public class QtEnumName extends ASTDelegatedName {
+public class QtEnumName extends AbstractQObjectFieldName implements IQtASTName {
- private final QObjectName qobjName;
- private final String qtEnumName;
private final IASTName cppEnumName;
- private final QtASTImageLocation location;
private final boolean isFlag;
- private ASTNodeProperty propertyInParent;
-
public QtEnumName(QObjectName qobjName, IASTName ast, String qtEnumName, IASTName cppEnumName, QtASTImageLocation location, boolean isFlag) {
- super(ast);
- this.qobjName = qobjName;
- this.qtEnumName = qtEnumName;
+ super(qobjName, ast, qtEnumName, location);
this.cppEnumName = cppEnumName;
- this.location = location;
this.isFlag = isFlag;
}
@@ -42,94 +26,7 @@ public class QtEnumName extends ASTDelegatedName {
}
@Override
- protected IBinding createBinding() {
- IBinding owner = qobjName.getBinding();
- QtBinding qobj = owner == null ? null : (QtBinding) owner.getAdapter(QtBinding.class);
- return new QtBinding(QtPDOMNodeType.QEnum, qobj, this, cppEnumName);
- }
-
- @Override
- public IASTFileLocation getFileLocation() {
- return location;
- }
-
- @Override
- public IASTNode getParent() {
- return qobjName;
- }
-
- @Override
- public IASTNode[] getChildren() {
- return IASTNode.EMPTY_NODE_ARRAY;
- }
-
- @Override
- public void setParent(IASTNode node) {
- throw new IllegalStateException("attempt to modify parent of Qt Enum"); //$NON-NLS-1$
- }
-
- @Override
- public ASTNodeProperty getPropertyInParent() {
- return propertyInParent;
- }
-
- @Override
- public void setPropertyInParent(ASTNodeProperty property) {
- propertyInParent = property;
- }
-
- @Override
- public char[] getSimpleID() {
- return qtEnumName.toCharArray();
- }
-
- @Override
- public String getRawSignature() {
- return qtEnumName;
- }
-
- @Override
- public IASTNode getOriginalNode() {
- return this;
- }
-
- @Override
- public boolean isDeclaration() {
- return false;
- }
-
- @Override
- public boolean isReference() {
- return false;
- }
-
- @Override
- public boolean isDefinition() {
- return true;
- }
-
- @Override
- public int getRoleOfName(boolean allowResolution) {
- return IASTNameOwner.r_definition;
- }
-
- @Override
- public ILinkage getLinkage() {
- return Linkage.QT_LINKAGE;
- }
-
- @Override
- public IASTImageLocation getImageLocation() {
- return location;
- }
-
- @Override
- public IASTName copy() {
- return copy(CopyStyle.withoutLocations);
- }
-
- @Override
- public IASTName copy(CopyStyle style) {
- return new QtEnumName(qobjName, delegate, qtEnumName, cppEnumName, location, isFlag);
+ public QtPDOMBinding createPDOMBinding(QtPDOMLinkage linkage) throws CoreException {
+ return new QtPDOMQEnum(linkage, getOwner(linkage), this, cppEnumName);
}
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMArray.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMArray.java
new file mode 100644
index 0000000..ea3f6a6
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMArray.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2013 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.qt.internal.core.pdom;
+
+import org.eclipse.cdt.internal.core.pdom.db.Database;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * A utility class for storing a fixed-size list of fixed-size elements into the Database.
+ */
+@SuppressWarnings("restriction")
+public class QtPDOMArray<T> {
+
+ private static int offsetInitializer = 0;
+ protected static enum Field {
+ Count(Database.INT_SIZE),
+ Data(0); // The size of the block is provided at runtime.
+
+ public final int offset;
+
+ private Field(int sizeof) {
+ this.offset = offsetInitializer;
+ offsetInitializer += sizeof;
+ }
+
+ public long getRecord(long baseRec) {
+ return baseRec + offset;
+ }
+ }
+
+ private final QtPDOMLinkage linkage;
+ private final IQtPDOMCodec<T> codec;
+ private long record;
+
+ public QtPDOMArray(QtPDOMLinkage linkage, IQtPDOMCodec<T> codec, long record) {
+ this.linkage = linkage;
+ this.codec = codec;
+ this.record = record;
+ }
+
+ public QtPDOMArray(QtPDOMLinkage linkage, IQtPDOMCodec<T> codec, T[] array) throws CoreException {
+ this.linkage = linkage;
+ this.codec = codec;
+ this.record = 0;
+ set(array);
+ }
+
+ public long getRecord() {
+ return record;
+ }
+
+ /**
+ * Return array that is stored in the database. Return null if the receiver is not initialized.
+ */
+ public T[] get() throws CoreException {
+ if (record == 0)
+ return null;
+
+ int count = linkage.getDB().getInt(Field.Count.getRecord(record));
+
+ long elementRec = Field.Data.getRecord(record);
+ T[] array = codec.allocArray(count);
+ for(int i = 0; i < count; ++i, elementRec += codec.getElementSize())
+ array[i] = codec.decode(linkage, elementRec);
+
+ return array;
+ }
+
+ /**
+ * Store the given array into the database. This may change the storage location of the
+ * receiver.
+ * @param array The array of elements that should be stored. Setting the value to null is
+ * equivalent to calling {@link #delete()}, which clears all storage.
+ */
+ public long set(T[] array) throws CoreException {
+ if (array == null)
+ return delete();
+
+ // Initialize the receiver if needed.
+ if (record == 0) {
+ record = linkage.getDB().malloc(Field.Data.offset + (array.length * codec.getElementSize()));
+ linkage.getDB().putInt(Field.Count.getRecord(record), array.length);
+ } else {
+ // Check if the storage block needs to be resized.
+ int count = linkage.getDB().getInt(Field.Count.getRecord(record));
+ if (count != array.length) {
+ linkage.getDB().free(record);
+ record = linkage.getDB().malloc(Field.Data.offset + (array.length * codec.getElementSize()));
+ linkage.getDB().putInt(Field.Count.getRecord(record), array.length);
+ }
+ }
+
+ // Write the new content into the database.
+ long elementRec = Field.Data.getRecord(record);
+ for(int i = 0; i < array.length; ++i, elementRec += codec.getElementSize())
+ codec.encode(linkage, elementRec, array[i]);
+
+ return record;
+ }
+
+ /**
+ * Release all storage used by the receiver and its elements.
+ */
+ public long delete() throws CoreException {
+ // If the receiver is already empty, then there is nothing else to do.
+ if (record == 0)
+ return record;
+
+ // Otherwise get the size of the stored array and delete each element.
+ int count = linkage.getDB().getInt(Field.Count.getRecord(record));
+ if (count > 0) {
+ long elementRec = Field.Data.getRecord(record);
+ for(int i = 0; i < count; ++i, elementRec += codec.getElementSize())
+ codec.encode(linkage, elementRec, null);
+ }
+
+ // Finally, release the entire record.
+ linkage.getDB().free(record);
+ record = 0;
+ return record;
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java
index e554890..293b797 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMBinding.java
@@ -7,13 +7,11 @@
*/
package org.eclipse.cdt.qt.internal.core.pdom;
-import org.eclipse.cdt.core.dom.ILinkage;
-import org.eclipse.cdt.core.dom.ast.IBinding;
+import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode;
-import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
@@ -40,39 +38,36 @@ public abstract class QtPDOMBinding extends PDOMBinding {
super(linkage, record);
}
- protected QtPDOMBinding(QtPDOMLinkage linkage, PDOMNode parent, QtBinding qtBinding) throws CoreException {
- super(linkage, parent, qtBinding.getNameCharArray());
- qtBinding.setPDOMBinding(this);
-
- getDB().putRecPtr(Field.CppRecord.getRecord(record), linkage.getCPPRecord(qtBinding));
+ protected QtPDOMBinding(QtPDOMLinkage linkage, PDOMNode parent, IASTName qtName) throws CoreException {
+ super(linkage, parent, qtName.getSimpleID());
}
@Override
protected int getRecordSize() {
return Field.Last.offset;
}
-
- public long getCppRecord() {
- try {
- return getDB().getRecPtr(Field.CppRecord.getRecord(record));
- } catch (CoreException e) {
- QtPlugin.log(e);
- }
-
- return 0;
- }
-
- public IBinding getCppBinding() throws CoreException {
- long cppRec = getCppRecord();
- if (cppRec == 0)
- return null;
-
- PDOMLinkage cppLinkage = getPDOM().getLinkage(ILinkage.CPP_LINKAGE_ID);
- if (cppLinkage == null)
- return null;
-
- return cppLinkage.getBinding(cppRec);
- }
+//
+// public long getCppRecord() {
+// try {
+// return getDB().getRecPtr(Field.CppRecord.getRecord(record));
+// } catch (CoreException e) {
+// QtPlugin.log(e);
+// }
+//
+// return 0;
+// }
+//
+// public IBinding getCppBinding() throws CoreException {
+// long cppRec = getCppRecord();
+// if (cppRec == 0)
+// return null;
+//
+// PDOMLinkage cppLinkage = getPDOM().getLinkage(ILinkage.CPP_LINKAGE_ID);
+// if (cppLinkage == null)
+// return null;
+//
+// return cppLinkage.getBinding(cppRec);
+// }
protected QtPDOMLinkage getQtLinkage() {
PDOMLinkage pdomLinkage = getLinkage();
@@ -87,6 +82,14 @@ public abstract class QtPDOMBinding extends PDOMBinding {
return super.getQualifiedName();
}
+ // Access to the base class is restricted in the cdt.core plugin. Other classes in the qt.core
+ // plugin that need the name get an access warning. This forwarding function moves those warnings
+ // to a single place (this method).
+ @Override
+ public String getName() {
+ return super.getName();
+ }
+
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public Object getAdapter(Class adapter) {
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMLinkage.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMLinkage.java
index 1f1e6c4..983de4e 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMLinkage.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMLinkage.java
@@ -7,6 +7,9 @@
*/
package org.eclipse.cdt.qt.internal.core.pdom;
+import java.util.HashMap;
+import java.util.Map;
+
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
@@ -14,10 +17,8 @@ import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.internal.core.dom.parser.ISerializableEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.ITypeMarshalBuffer;
import org.eclipse.cdt.internal.core.pdom.PDOM;
-import org.eclipse.cdt.internal.core.pdom.db.BTree;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.cdt.internal.core.pdom.db.IBTreeComparator;
-import org.eclipse.cdt.internal.core.pdom.db.IBTreeVisitor;
import org.eclipse.cdt.internal.core.pdom.dom.FindBinding;
import org.eclipse.cdt.internal.core.pdom.dom.IPDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
@@ -32,7 +33,6 @@ public class QtPDOMLinkage extends PDOMLinkage {
private static int offsetInitializer = PDOMLinkage.RECORD_SIZE;
private static enum Field {
Version(Database.INT_SIZE),
- CppIndex(Database.PTR_SIZE),
Last(0);
private final int offset;
@@ -50,16 +50,12 @@ public class QtPDOMLinkage extends PDOMLinkage {
// The version that has been read from/written to the persisted file.
private int version;
- // An index of C++ -> Qt Bindings. This is used for fast lookup of things like the QObject
- // for a C++ class in the base specifier list. Each entry in the index is a pair like
- // { CPP_Record, Qt_Record }, the CPP_Record is used for comparison when searching the index.
- private final BTree cppIndex;
+ private final Map<IQtASTName, PDOMBinding> cache = new HashMap<IQtASTName, PDOMBinding>();
public QtPDOMLinkage(PDOM pdom, long record) throws CoreException {
super(pdom, record);
version = pdom.getDB().getInt(Field.Version.getRecord(record));
- cppIndex = new BTree(pdom.getDB(), Field.CppIndex.getRecord(record), new CppRecordIndexComparator());
}
protected QtPDOMLinkage(PDOM pdom) throws CoreException {
@@ -68,11 +64,6 @@ public class QtPDOMLinkage extends PDOMLinkage {
// Initialize the version with whatever is current.
version = QtPDOMNodeType.VERSION;
pdom.getDB().putInt(Field.Version.getRecord(record), version);
-
- long cppIndexRec = Field.CppIndex.getRecord(record);
- Database db = pdom.getDB();
- db.putRecPtr(cppIndexRec, 0);
- cppIndex = new BTree(db, cppIndexRec, new CppRecordIndexComparator());
}
public int getVersion() {
@@ -89,16 +80,6 @@ public class QtPDOMLinkage extends PDOMLinkage {
return ILinkage.QT_LINKAGE_ID;
}
- public QtPDOMBinding findFromCppRecord(long cppRec) throws CoreException {
- CppRecordIndexFinder finder = new CppRecordIndexFinder(cppRec);
- cppIndex.accept(finder);
- if (finder.foundQtRec == null)
- return null;
-
- PDOMNode node = getNode(finder.foundQtRec.longValue());
- return node instanceof QtPDOMBinding ? (QtPDOMBinding) node : null;
- }
-
@Override
public PDOMNode getNode(long record, int nodeType) throws CoreException {
return QtPDOMNodeType.load(this, nodeType, record);
@@ -123,18 +104,6 @@ public class QtPDOMLinkage extends PDOMLinkage {
&& pdomBinding.getLinkage() == this)
return pdomBinding;
- // Otherwise try to create a new PDOMBinding.
- QtBinding qtBinding = (QtBinding) binding.getAdapter(QtBinding.class);
- if (qtBinding != null)
- switch(qtBinding.getType()) {
- case QObject:
- pdomBinding = new QtPDOMQObject(this, qtBinding);
- break;
- case QEnum:
- pdomBinding = new QtPDOMQEnum(this, adaptBinding(qtBinding.getOwner()), qtBinding);
- break;
- }
-
// If a PDOMBinding was created, then add it to the linkage before returning it.
if (pdomBinding != null) {
addChild(pdomBinding);
@@ -145,19 +114,8 @@ public class QtPDOMLinkage extends PDOMLinkage {
return getPDOM().getLinkage(ILinkage.CPP_LINKAGE_ID).adaptBinding(binding);
}
- public void addChild(QtPDOMBinding child) throws CoreException {
- super.addChild(child);
-
- Database db = getDB();
- long pair = db.malloc(Database.PTR_SIZE * 2);
- db.putRecPtr(pair, child.getCppRecord());
- db.putRecPtr(pair + Database.PTR_SIZE, child.getRecord());
- cppIndex.insert(pair);
- }
+ public long getCPPRecord(IASTName cppName) throws CoreException {
- public long getCPPRecord(QtBinding qtBinding) throws CoreException {
-
- IASTName cppName = qtBinding.getCppName();
if (cppName == null)
return 0;
@@ -176,14 +134,69 @@ public class QtPDOMLinkage extends PDOMLinkage {
return pdomBinding.getRecord();
}
+ /**
+ * Return the PDOMBinding for the given Qt name creating a new binding if needed. The
+ * implementation caches the result using the name instance as the key. This ensures
+ * one-to-one uniqueness between AST names and PDOMBindings.
+ * <p>
+ * This method is not thread-safe.
+ */
+ public PDOMBinding getBinding(IQtASTName qtAstName) throws CoreException {
+ // The Qt implementation ensures uniqueness by creating only a single instance of
+ // the IASTName for each thing that should create a single instance in the PDOM.
+ // This will work as long as all Qt elements are updated at once, which is currently
+ // the case.
+ //
+ // I don't think this needs to be thread-safe, because things are only added from
+ // the single indexer task.
+ PDOMBinding pdomBinding = null;
+ pdomBinding = cache.get(qtAstName);
+ if (pdomBinding != null)
+ return pdomBinding;
+
+ // The result is cached even when null is returned.
+ pdomBinding = qtAstName.createPDOMBinding(this);
+ cache.put(qtAstName, pdomBinding);
+
+ // Only add children that are actually created.
+ if (pdomBinding != null)
+ addChild(pdomBinding);
+
+ return pdomBinding;
+ }
+
@Override
public PDOMBinding addBinding(IASTName name) throws CoreException {
- return name == null ? null : adaptBinding(name.getBinding());
+
+ // The Qt linkage is able to reference elements in other linkages. This implementation
+ // needs to decide if the binding associated with this name is from the Qt linkage or
+ // from one of those external references.
+
+ if (name == null)
+ return null;
+
+ if (name instanceof IQtASTName)
+ return getBinding((IQtASTName) name);
+
+ IBinding binding = name.getBinding();
+ if (binding == null)
+ return null;
+
+ // Use the receiving linkage by default, and override only if the binding is found to
+ // have a linkage with a different id.
+ PDOMLinkage pdomLinkage = this;
+ ILinkage linkage = binding.getLinkage();
+ if (linkage != null
+ && linkage.getLinkageID() != getLinkageID())
+ pdomLinkage = getPDOM().getLinkage(linkage.getLinkageID());
+
+ // Handle bindings in unknown linkages as though the name is to be added to this linkage.
+ return (pdomLinkage == null ? this : pdomLinkage).adaptBinding(binding);
}
@Override
public int getBindingType(IBinding binding) {
- return binding instanceof QtBinding ? ((QtBinding) binding).getType().Type : 0;
+ return binding instanceof QtPDOMBinding ? ((QtPDOMBinding) binding).getNodeType() : 0;
}
@Override
@@ -205,44 +218,4 @@ public class QtPDOMLinkage extends PDOMLinkage {
public ISerializableEvaluation unmarshalEvaluation(ITypeMarshalBuffer typeMarshalBuffer) throws CoreException {
throw new CoreException(QtPlugin.error("Qt Linkage does not marshal evaluations")); //$NON-NLS-1$
}
-
- private class CppRecordIndexComparator implements IBTreeComparator {
-
- @Override
- public int compare(long record1, long record2) throws CoreException {
- Database db = getDB();
-
- Long cppRec1 = Long.valueOf(db.getRecPtr(record1));
- long cppRec2 = Long.valueOf(db.getRecPtr(record2));
- return cppRec1.compareTo(cppRec2);
- }
- }
-
- private class CppRecordIndexFinder extends CppRecordIndexComparator implements IBTreeVisitor {
-
- private final Long targetCppRec;
- public Long foundQtRec;
-
- public CppRecordIndexFinder(long targetCppRec) {
- this.targetCppRec = Long.valueOf(targetCppRec);
- }
-
- @Override
- public int compare(long record) throws CoreException {
- Long cppRec = Long.valueOf(getDB().getRecPtr(record));
- return cppRec.compareTo(targetCppRec);
- }
-
- @Override
- public boolean visit(long record) throws CoreException {
- // Stop searching after the record is found.
- if (foundQtRec != null)
- return false;
-
- // The record is the pair, so the Qt rec is the second element.
- long qtRec = getDB().getRecPtr(record + Database.PTR_SIZE);
- foundQtRec = Long.valueOf(qtRec);
- return false;
- }
- }
}
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 4db7344..ab4f4ec 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
@@ -15,7 +15,9 @@ import org.eclipse.core.runtime.CoreException;
public enum QtPDOMNodeType {
QObject,
- QEnum;
+ QEnum,
+ QProperty,
+ QPropertyAttribute;
public final int Type = IIndexBindingConstants.LAST_CONSTANT + 1 + ordinal();
@@ -26,7 +28,7 @@ public enum QtPDOMNodeType {
* <p>
* The version is needed because ordinals for these enumerators are written to the file.
*/
- public static final int VERSION = 2;
+ public static final int VERSION = 3;
public static QtPDOMNodeType forType(int version, int type) {
// Nothing has been deleted or replaced yet, so the version is ignored.
@@ -48,6 +50,8 @@ public enum QtPDOMNodeType {
return new QtPDOMQObject(linkage, record);
case QEnum:
return new QtPDOMQEnum(linkage, record);
+ case QProperty:
+ return new QtPDOMProperty(linkage, record);
}
return null;
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMProperty.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMProperty.java
new file mode 100644
index 0000000..a414295
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMProperty.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2013 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.qt.internal.core.pdom;
+
+import org.eclipse.cdt.core.dom.ast.IBinding;
+import org.eclipse.cdt.core.dom.ast.ICompositeType;
+import org.eclipse.cdt.core.dom.ast.IField;
+import org.eclipse.cdt.core.dom.ast.IType;
+import org.eclipse.cdt.core.dom.ast.IValue;
+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.dom.PDOMBinding;
+import org.eclipse.cdt.qt.core.QtPlugin;
+import org.eclipse.cdt.qt.core.index.IQProperty;
+import org.eclipse.core.runtime.CoreException;
+
+@SuppressWarnings("restriction")
+public class QtPDOMProperty extends QtPDOMBinding implements IField {
+
+ private static int offsetInitializer = QtPDOMBinding.Field.Last.offset;
+ protected static enum Field {
+ Type(Database.PTR_SIZE),
+ Attributes(Database.PTR_SIZE),
+ Last(0);
+
+ public final int offset;
+
+ private Field(int sizeof) {
+ this.offset = offsetInitializer;
+ offsetInitializer += sizeof;
+ }
+
+ public long getRecord(long baseRec) {
+ return baseRec + offset;
+ }
+ }
+
+ private QtPDOMQObject qobj;
+
+ public QtPDOMProperty(QtPDOMLinkage linkage, long record) {
+ super(linkage, record);
+ }
+
+ public QtPDOMProperty(QtPDOMLinkage linkage, PDOMBinding parent, QtPropertyName qtName) throws CoreException {
+ super(linkage, parent, qtName);
+
+ setType(qtName.getType());
+
+ if (!(parent instanceof QtPDOMQObject))
+ this.qobj = null;
+ else {
+ this.qobj = (QtPDOMQObject) parent;
+ this.qobj.addChild(this);
+ }
+ }
+
+ @Override
+ protected int getRecordSize() {
+ return Field.Last.offset;
+ }
+
+ @Override
+ public int getNodeType() {
+ return QtPDOMNodeType.QProperty.Type;
+ }
+
+ public void delete() throws CoreException {
+ long fieldRec = getDB().getRecPtr(Field.Type.getRecord(record));
+ if (fieldRec != 0)
+ getDB().getString(fieldRec).delete();
+ getDB().putRecPtr(Field.Type.getRecord(record), 0);
+ }
+
+ public void setType(String type) throws CoreException {
+ long rec = getDB().getRecPtr(Field.Type.getRecord(record));
+ if (rec != 0) {
+ IString typeStr = getDB().getString(rec);
+ if (type == null) {
+ typeStr.delete();
+ return;
+ }
+
+ // There is nothing to do if the database already stores the same name.
+ if (type.equals(typeStr.getString()))
+ return;
+ }
+
+ getDB().putRecPtr(Field.Type.getRecord(record), getDB().newString(type).getRecord());
+ }
+
+ // TODO IType?
+ public String getTypeStr() throws CoreException {
+ long rec = getDB().getRecPtr(Field.Type.getRecord(record));
+ if (rec == 0)
+ return null;
+
+ return getDB().getString(rec).getString();
+ }
+
+ public void setAttributes(Attribute[] attributes) throws CoreException {
+ long rec = getDB().getRecPtr(Field.Attributes.getRecord(record));
+ QtPDOMArray<Attribute> pdomArray = new QtPDOMArray<Attribute>(getQtLinkage(), Attribute.Codec, rec);
+ rec = pdomArray.set(attributes);
+ getDB().putRecPtr(Field.Attributes.getRecord(record), rec);
+ }
+
+ public Attribute[] getAttributes() throws CoreException {
+ long rec = getDB().getRecPtr(Field.Attributes.getRecord(record));
+ QtPDOMArray<Attribute> pdomArray = new QtPDOMArray<Attribute>(getQtLinkage(), Attribute.Codec, rec);
+ return pdomArray.get();
+ }
+
+ @Override
+ public ICompositeType getCompositeTypeOwner() {
+ if (qobj == null)
+ try {
+ IBinding parent = getParentBinding();
+ if (parent instanceof QtPDOMQObject)
+ qobj = (QtPDOMQObject) parent;
+ } catch(CoreException e) {
+ QtPlugin.log(e);
+ }
+
+ return qobj;
+ }
+
+ /**
+ * TODO use the real type?
+ */
+ private static final IType Type = new IType() {
+ @Override
+ public Object clone() {
+ // This is a stateless singleton instance, there is nothing to clone.
+ return this;
+ }
+
+ @Override
+ public boolean isSameType(IType type) {
+ return type == this;
+ }
+ };
+
+ @Override
+ public IType getType() {
+ return Type;
+ }
+
+ @Override
+ public IValue getInitialValue() {
+ return null;
+ }
+
+ @Override
+ public boolean isStatic() {
+ return false;
+ }
+
+ @Override
+ public boolean isExtern() {
+ return false;
+ }
+
+ @Override
+ public boolean isAuto() {
+ return false;
+ }
+
+ @Override
+ public boolean isRegister() {
+ return false;
+ }
+
+ public static class Attribute {
+ public final IQProperty.Attribute attr;
+ public final String value;
+ public final long cppRecord;
+
+ public Attribute(IQProperty.Attribute attr, String value) {
+ this.attr = attr;
+ this.value = value;
+ this.cppRecord = 0;
+ }
+
+ public Attribute(IQProperty.Attribute attr, String value, PDOMBinding cppBinding) {
+ this.attr = attr;
+ this.value = value;
+ this.cppRecord = cppBinding == null ? 0 : cppBinding.getRecord();
+ }
+
+ private Attribute(IQProperty.Attribute attr, String value, long cppRecord) {
+ this.attr = attr;
+ this.value = value;
+ this.cppRecord = cppRecord;
+ }
+
+ private static final IQtPDOMCodec<Attribute> Codec = new IQtPDOMCodec<Attribute>() {
+ @Override
+ public int getElementSize() {
+ return 1 + Database.PTR_SIZE + Database.PTR_SIZE;
+ }
+
+ @Override
+ public Attribute[] allocArray(int count) {
+ return new Attribute[count];
+ }
+
+ @Override
+ public Attribute decode(QtPDOMLinkage linkage, long record) throws CoreException {
+ byte attrId = linkage.getDB().getByte(record);
+ long valRec = linkage.getDB().getRecPtr(record + 1);
+ long cppRec = linkage.getDB().getRecPtr(record + 1 + Database.PTR_SIZE);
+
+ if (attrId < 0 || attrId >= IQProperty.Attribute.values().length)
+ throw QtPlugin.coreException("invalid QProperty attribute id read from datbase, was " + attrId);
+
+ IQProperty.Attribute attr = IQProperty.Attribute.values()[attrId];
+
+ String val = valRec == 0 ? "" : linkage.getDB().getString(valRec).getString();
+ return new Attribute(attr, val, cppRec);
+ }
+
+ @Override
+ public void encode(QtPDOMLinkage linkage, long record, Attribute element) throws CoreException {
+ linkage.getDB().putByte(record, (byte) element.attr.ordinal());
+
+ // Delete the existing strings then create and store new ones.
+ long rec = linkage.getDB().getRecPtr(record + 1);
+ if (rec != 0)
+ linkage.getDB().getString(rec).delete();
+
+ if (element == null || element.value == null)
+ linkage.getDB().putRecPtr(record + 1, 0);
+ else
+ linkage.getDB().putRecPtr(record + 1, linkage.getDB().newString(element.value).getRecord());
+
+ linkage.getDB().putRecPtr(record + 1 + Database.PTR_SIZE, element.cppRecord);
+ }
+ };
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQEnum.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQEnum.java
index c281399..753f5f0 100644
--- a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQEnum.java
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPDOMQEnum.java
@@ -11,6 +11,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
@@ -19,7 +20,9 @@ import org.eclipse.cdt.core.dom.ast.IEnumerator;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IValue;
+import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
+import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.core.runtime.CoreException;
@@ -29,6 +32,7 @@ public class QtPDOMQEnum extends QtPDOMBinding implements IField {
private static int offsetInitializer = QtPDOMBinding.Field.Last.offset;
protected static enum Field {
Flags(1),
+ CppRecord(Database.PTR_SIZE),
Last(0);
public final int offset;
@@ -51,13 +55,14 @@ public class QtPDOMQEnum extends QtPDOMBinding implements IField {
super(linkage, record);
}
- public QtPDOMQEnum(QtPDOMLinkage linkage, PDOMBinding parent, QtBinding binding) throws CoreException {
- super(linkage, parent, binding);
+ public QtPDOMQEnum(QtPDOMLinkage linkage, PDOMBinding parent, IASTName qtName, IASTName cppName) throws CoreException {
+ super(linkage, parent, qtName);
+
+ getDB().putRecPtr(Field.CppRecord.getRecord(record), linkage.getCPPRecord(cppName));
// The flags are set in several sections, and then written to the Database in one operation.
byte flags = 0;
- IASTName qtName = binding.getQtName();
if (qtName instanceof QtEnumName
&& ((QtEnumName) qtName).isFlag())
flags |= IS_FLAG_MASK;
@@ -78,6 +83,25 @@ public class QtPDOMQEnum extends QtPDOMBinding implements IField {
return Field.Last.offset;
}
+ public IEnumeration getCppEnumeration() throws CoreException {
+ long cppRec = getDB().getRecPtr(Field.CppRecord.getRecord(record));
+ if (cppRec == 0)
+ return null;
+
+ PDOMLinkage cppLinkage = getPDOM().getLinkage(ILinkage.CPP_LINKAGE_ID);
+ if (cppLinkage == null)
+ return null;
+
+ PDOMBinding cppBinding = cppLinkage.getBinding(cppRec);
+
+ // TODO
+ if (cppBinding == null)
+ return null;
+ cppBinding.getAdapter(IEnumeration.class);
+
+ return cppBinding instanceof IEnumeration ? (IEnumeration) cppBinding : null;
+ }
+
public boolean isFlag() throws CoreException {
byte flags = getDB().getByte(Field.Flags.getRecord(record));
return (flags & IS_FLAG_MASK) == IS_FLAG_MASK;
@@ -103,12 +127,8 @@ public class QtPDOMQEnum extends QtPDOMBinding implements IField {
}
public List<IEnumerator> getEnumerators() throws CoreException {
- IBinding cppBinding = getCppBinding();
- if (!(cppBinding instanceof IEnumeration))
- return Collections.emptyList();
-
- IEnumeration cppEnum = (IEnumeration) cppBinding;
- return Arrays.asList(cppEnum.getEnumerators());
+ IEnumeration cppEnum = getCppEnumeration();
+ return cppEnum == null ? Collections.<IEnumerator>emptyList() : Arrays.asList(cppEnum.getEnumerators());
}
/**
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 21222d1..675b053 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,10 +9,12 @@ package org.eclipse.cdt.qt.internal.core.pdom;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
@@ -22,9 +24,10 @@ 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.PDOMBinding;
+import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMNode;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.core.runtime.CoreException;
@@ -41,20 +44,31 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
// The record size is retrieved as the offset of the special terminal enumerator Last.
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);
+ CppRecord(Database.PTR_SIZE, 3),
+ Children(4 /* From PDOMNodeLinkedList.RECORD_SIZE, which is protected */, 0),
+ ClassInfos(Database.PTR_SIZE, 2),
+ Properties(Database.PTR_SIZE, 3),
+ Last(0, 0);
private final int offset;
+ private final int version;
- private Field(int sizeof) {
+ private Field(int sizeof, int version) {
this.offset = offsetInitializer;
+ this.version = version;
offsetInitializer += sizeof;
}
public long getRecord(long baseRec) {
return baseRec + offset;
}
+
+ /**
+ * Return true if this linkage in supported in the given instance of the linkage.
+ */
+ public boolean isSupportedIn(QtPDOMLinkage linkage) {
+ return linkage.getVersion() >= version;
+ }
}
private final PDOMNodeLinkedList children;
@@ -64,77 +78,92 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
children = new PDOMNodeLinkedList(linkage, Field.Children.getRecord(record));
}
- public QtPDOMQObject(QtPDOMLinkage linkage, QtBinding binding) throws CoreException {
- super(linkage, null, binding);
+ public QtPDOMQObject(QtPDOMLinkage linkage, IASTName qtName, IASTName cppName) throws CoreException {
+ super(linkage, null, qtName);
+
+ IBinding cppBinding = getPDOM().findBinding(cppName);
+ if (cppBinding != null) {
+ IPDOMBinding cppPDOMBinding = (IPDOMBinding) cppBinding.getAdapter(IPDOMBinding.class);
+ if (cppPDOMBinding != null) {
+ if (cppPDOMBinding.getLinkage() != null
+ && cppPDOMBinding.getLinkage().getLinkageID() == ILinkage.CPP_LINKAGE_ID)
+ getDB().putRecPtr(Field.CppRecord.getRecord(record), cppPDOMBinding.getRecord());
+ }
+ }
+
children = new PDOMNodeLinkedList(linkage, Field.Children.getRecord(record));
- IASTName qtName = binding.getQtName();
- if (qtName instanceof QObjectName)
- setClassInfos(((QObjectName) qtName).getClassInfos());
+ if (qtName instanceof QObjectName) {
+ QObjectName qobjName = (QObjectName) qtName;
+ setClassInfos(qobjName.getClassInfos());
+ }
}
- public void setClassInfos(Map<String, String> classInfos) throws CoreException {
+ public void delete() throws CoreException {
+ long fieldRec = Field.ClassInfos.getRecord(record);
+ new QtPDOMArray<ClassInfo>(getQtLinkage(), ClassInfo.Codec, fieldRec).delete();
+ getDB().putRecPtr(Field.ClassInfos.getRecord(record), 0);
+ }
- // ClassInfo was not supported before version 2.
- if (getQtLinkage().getVersion() < 2)
- return;
+ public ICPPClassType getCppClassType() throws CoreException {
+ long cppRec = getDB().getRecPtr(Field.CppRecord.getRecord(record));
+ if (cppRec == 0)
+ return null;
- // 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);
- }
+ PDOMLinkage cppLinkage = getPDOM().getLinkage(ILinkage.CPP_LINKAGE_ID);
+ if (cppLinkage == null)
+ return null;
- // Clear the pointer if the incoming map is empty.
- if (classInfos.isEmpty()) {
- getDB().putRecPtr(Field.ClassInfos.getRecord(record), 0);
- return;
- }
+ PDOMBinding cppBinding = cppLinkage.getBinding(cppRec);
+
+ // TODO
+ if (cppBinding == null)
+ return null;
+ cppBinding.getAdapter(ICPPClassType.class);
+
+ return cppBinding instanceof ICPPClassType ? (ICPPClassType) cppBinding : null;
+ }
- // 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());
+ public void setClassInfos(Map<String, String> classInfos) throws CoreException {
- 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());
+ // Make sure the version of the linkage contains this field.
+ if (!Field.ClassInfos.isSupportedIn(getQtLinkage()))
+ return;
- getDB().putRecPtr(b, key.getRecord()); b += Database.PTR_SIZE;
- getDB().putRecPtr(b, val.getRecord()); b += Database.PTR_SIZE;
+ // Create an array to be stored to the PDOM.
+ ClassInfo[] array = new ClassInfo[classInfos.size()];
+ Iterator<Map.Entry<String, String>> iterator = classInfos.entrySet().iterator();
+ for(int i = 0; i < array.length && iterator.hasNext(); ++i) {
+ Map.Entry<String, String> entry = iterator.next();
+ array[i] = new ClassInfo(entry.getKey(), entry.getValue());
}
- // Put the new block into the PDOM.
- getDB().putRecPtr(Field.ClassInfos.getRecord(record), block);
+ // Store the array into the Database.
+ long arrayRec = getDB().getRecPtr(Field.ClassInfos.getRecord(record));
+ QtPDOMArray<ClassInfo> pdomArray = new QtPDOMArray<QtPDOMQObject.ClassInfo>(getQtLinkage(), ClassInfo.Codec, arrayRec);
+ arrayRec = pdomArray.set(array);
+
+ // Update the record that is stored in the receiver's field.
+ getDB().putRecPtr(Field.ClassInfos.getRecord(record), arrayRec);
}
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)
+ // Make sure the version of the linkage contains this field.
+ if (!Field.ClassInfos.isSupportedIn(getQtLinkage()))
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;
+ // Read the array from the Database and insert the elements into the Map that is to be returned.
+ long arrayRec = getDB().getRecPtr(Field.ClassInfos.getRecord(record));
+ QtPDOMArray<ClassInfo> pdomArray = new QtPDOMArray<QtPDOMQObject.ClassInfo>(getQtLinkage(), ClassInfo.Codec, arrayRec);
- rec = getDB().getRecPtr(block);
- IString val = getDB().getString(rec);
- block += Database.PTR_SIZE;
+ ClassInfo[] array = pdomArray.get();
+ if (array == null)
+ return classInfos;
- classInfos.put(key.getString(), val.getString());
- }
- }
+ for(ClassInfo classInfo : array)
+ classInfos.put(classInfo.key, classInfo.value);
return classInfos;
}
@@ -157,12 +186,12 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
}
public List<QtPDOMQObject> findBases() throws CoreException {
- IBinding cppBinding = getCppBinding();
- if (!(cppBinding instanceof ICPPClassType))
+ ICPPClassType cppClassType = getCppClassType();
+ if (cppClassType == null)
return Collections.emptyList();
List<QtPDOMQObject> bases = new ArrayList<QtPDOMQObject>();
- for (ICPPBase base : ((ICPPClassType) cppBinding).getBases()) {
+ for (ICPPBase base : cppClassType.getBases()) {
if (base.getVisibility() != ICPPBase.v_public)
continue;
@@ -170,18 +199,12 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
if (baseCls == null)
continue;
- IPDOMBinding pdomBase = (IPDOMBinding) baseCls.getAdapter(IPDOMBinding.class);
- if (pdomBase == null)
- continue;
-
- QtPDOMBinding qtPDOMBinding = getQtLinkage().findFromCppRecord(pdomBase.getRecord());
- if (qtPDOMBinding == null)
- continue;
-
- QtPDOMQObject pdomQObj = (QtPDOMQObject) qtPDOMBinding.getAdapter(QtPDOMQObject.class);
- if (pdomQObj != null)
- bases.add(pdomQObj);
+ PDOMBinding pdomBinding = (PDOMBinding) baseCls.getAdapter(PDOMBinding.class);
+ QtPDOMQObject baseQObj = ASTNameReference.findFromBinding(QtPDOMQObject.class, pdomBinding);
+ if (baseQObj != null)
+ bases.add(baseQObj);
}
+
return bases;
}
@@ -213,6 +236,18 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
children.addMember(child);
}
+ public <T extends IField> List<T> getFields(Class<T> cls) throws CoreException {
+ QtPDOMVisitor.All<T> collector = new QtPDOMVisitor.All<T>(cls);
+ try {
+ children.accept(collector);
+ } catch(CoreException e) {
+ QtPlugin.log(e);
+ return Collections.emptyList();
+ }
+
+ return collector.list;
+ }
+
@Override
public IField[] getFields() {
QtPDOMVisitor.All<IField> collector = new QtPDOMVisitor.All<IField>(IField.class);
@@ -241,9 +276,7 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
@Override
public IScope getCompositeScope() {
try {
- IBinding cppBinding = getCppBinding();
- if (cppBinding instanceof ICompositeType)
- return ((ICompositeType) cppBinding).getCompositeScope();
+ return getCppClassType().getCompositeScope();
} catch(CoreException e) {
QtPlugin.log(e);
}
@@ -255,4 +288,46 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
public Object clone() {
throw new UnsupportedOperationException();
}
+
+ private static class ClassInfo {
+ public final String key;
+ public final String value;
+ public ClassInfo(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public static final IQtPDOMCodec<ClassInfo> Codec = new IQtPDOMCodec<ClassInfo>() {
+ @Override
+ public int getElementSize() {
+ return 2 * Database.PTR_SIZE;
+ }
+
+ @Override
+ public ClassInfo[] allocArray(int count) {
+ return new ClassInfo[count];
+ }
+
+ @Override
+ public ClassInfo decode(QtPDOMLinkage linkage, long record) throws CoreException {
+ long keyRec = linkage.getDB().getRecPtr(record);
+ long valRec = linkage.getDB().getRecPtr(record + Database.PTR_SIZE);
+ return new ClassInfo(linkage.getDB().getString(keyRec).getString(), linkage.getDB().getString(valRec).getString());
+ }
+
+ @Override
+ public void encode(QtPDOMLinkage linkage, long record, ClassInfo element) throws CoreException {
+ // Delete the existing strings then create and store new ones.
+ long rec = linkage.getDB().getRecPtr(record);
+ if (rec != 0)
+ linkage.getDB().getString(rec).delete();
+ linkage.getDB().putRecPtr(record, element == null ? 0 : linkage.getDB().newString(element.key).getRecord());
+
+ rec = linkage.getDB().getRecPtr(record + Database.PTR_SIZE);
+ if (rec != 0)
+ linkage.getDB().getString(rec).delete();
+ linkage.getDB().putRecPtr(record + Database.PTR_SIZE, element == null ? 0 : linkage.getDB().newString(element.value).getRecord());
+ }
+ };
+ }
}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPropertyAttributeName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPropertyAttributeName.java
new file mode 100644
index 0000000..7739e76
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPropertyAttributeName.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.qt.internal.core.pdom;
+
+import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
+import org.eclipse.cdt.core.dom.ast.IASTName;
+import org.eclipse.core.runtime.CoreException;
+
+public class QtPropertyAttributeName extends ASTDelegatedName implements IQtASTName {
+
+ private final QtASTImageLocation location;
+
+ public QtPropertyAttributeName(IASTName ast, String name, QtASTImageLocation location) {
+ super(ast);
+ this.location = location;
+ }
+
+ @Override
+ public IASTFileLocation getFileLocation() {
+ return location;
+ }
+
+ @Override
+ public QtPDOMBinding createPDOMBinding(QtPDOMLinkage linkage) throws CoreException {
+ return null;
+ }
+}
diff --git a/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPropertyName.java b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPropertyName.java
new file mode 100644
index 0000000..5f3ce6f
--- /dev/null
+++ b/qt/org.eclipse.cdt.qt.core/src/org/eclipse/cdt/qt/internal/core/pdom/QtPropertyName.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013 QNX Software Systems and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.eclipse.cdt.qt.internal.core.pdom;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.cdt.core.dom.ast.IASTName;
+import org.eclipse.cdt.core.dom.ast.IBinding;
+import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
+import org.eclipse.cdt.qt.core.index.IQProperty;
+import org.eclipse.core.runtime.CoreException;
+
+@SuppressWarnings("restriction")
+public class QtPropertyName extends AbstractQObjectFieldName implements IQtASTName {
+
+ private String type;
+ private List<QtPDOMProperty.Attribute> attributes = new ArrayList<QtPDOMProperty.Attribute>();
+
+ public QtPropertyName(QObjectName qobjName, IASTName ast, String name, QtASTImageLocation location) {
+ super(qobjName, ast, name, location);
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ /**
+ * This permits storage of duplicate attributes, a Codan checker should flag this as an error, but
+ * while the invalid code exists, the references should continue to be properly resolved.
+ */
+ public void addAttribute(IQProperty.Attribute attr, String value) {
+ attributes.add(new QtPDOMProperty.Attribute(attr, value));
+ }
+
+ /**
+ * This permits storage of duplicate attributes, a Codan checker should flag this as an error, but
+ * while the invalid code exists, the references should continue to be properly resolved.
+ */
+ public void addAttribute(IQProperty.Attribute attr, String value, IBinding cppBinding) {
+ PDOMBinding pdomBinding = cppBinding == null ? null : (PDOMBinding) cppBinding.getAdapter(PDOMBinding.class);
+ attributes.add(new QtPDOMProperty.Attribute(attr, value, pdomBinding));
+ }
+
+ @Override
+ public QtPDOMBinding createPDOMBinding(QtPDOMLinkage linkage) throws CoreException {
+ QtPDOMProperty pdom = new QtPDOMProperty(linkage, getOwner(linkage), this);
+ pdom.setAttributes(attributes.toArray(new QtPDOMProperty.Attribute[attributes.size()]));
+ return pdom;
+ }
+}
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 9dbde17..cd5ea29 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
@@ -7,11 +7,18 @@
*/
package org.eclipse.cdt.qt.tests;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
import org.eclipse.cdt.qt.core.index.IQEnum;
import org.eclipse.cdt.qt.core.index.IQObject;
+import org.eclipse.cdt.qt.core.index.IQProperty;
+import org.eclipse.cdt.qt.core.index.IQProperty.Attribute;
import org.eclipse.cdt.qt.core.index.QtIndex;
public class QObjectTests extends BaseQtTestCase {
@@ -48,41 +55,6 @@ 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 {};
@@ -210,4 +182,237 @@ public class QObjectTests extends BaseQtTestCase {
fail("unexpected Q_FLAGS " + qEnum.getName());
}
}
+
+ // #include "junit-QObject.hh"
+ // class B : public QObject
+ // {
+ // Q_OBJECT
+ // Q_PROPERTY(bool allowed READ isAllowed)
+ // public:
+ // bool isAllowed() const { return false; }
+ // };
+ public void testOwner() throws Exception {
+ loadComment("owner.hh");
+
+ QtIndex qtIndex = QtIndex.getIndex(fProject);
+ assertNotNull(qtIndex);
+
+ IQObject qobj = qtIndex.findQObject(new String[]{ "B" });
+ if (!isIndexOk("B", qobj))
+ return;
+ assertNotNull(qobj);
+
+ Collection<IQProperty> properties = qobj.getProperties().locals();
+ assertNotNull(properties);
+ assertEquals(1, properties.size());
+
+ IQProperty property = properties.iterator().next();
+ assertNotNull(property);
+ assertTrue(qobj == property.getOwner());
+ }
+
+ // #include "junit-QObject.hh"
+ // template <typename T> class T {};
+ // class Q : public QObject
+ // {
+ // Q_OBJECT
+ //
+ // bool getProp() const;
+ //
+ // // strange cases found by grep'ing for Q_PROPERTY in the qt4 headers
+ // Q_PROPERTY( bool prop1 READ getProp )
+ // Q_PROPERTY( T<bool> prop2 READ getProp )
+ // Q_PROPERTY( T<bool *> prop3 READ getProp )
+ // Q_PROPERTY( bool *prop4 READ getProp )
+ // Q_PROPERTY( bool prop5 )
+ // Q_PROPERTY( bool *prop6 )
+ //
+ // Q_PROPERTY( bool read1 READ readMethod )
+ // Q_PROPERTY( bool read2 READ readMethod2 FINAL )
+ //
+ // // from qtoolbar.h
+ // Q_PROPERTY( Namespace::Type allowedAreas1 )
+ // Q_PROPERTY( Qt::ToolBarAreas allowedAreas2 READ allowedAreas WRITE setAllowedAreas
+ // DESIGNABLE (qobject_cast<QMainWindow *>(parentWidget()) != 0)
+ // NOTIFY allowedAreasChanged )
+ //
+ // bool readMethod();
+ // bool readMethod2() const { return false; }
+ // Qt::ToolBarAreas allowedAreas() const;
+ // void setAllowedAreas( Qt::ToolBarAreas ) { }
+ // Q_SIGNAL void allowedAreasChanged();
+ // };
+ public void testQProperties() throws Exception {
+ loadComment("q_property.hh");
+
+ QtIndex qtIndex = QtIndex.getIndex(fProject);
+ assertNotNull(qtIndex);
+
+ IQObject qobj = qtIndex.findQObject(new String[]{ "Q" });
+ if (!isIndexOk("Q", qobj))
+ return;
+ assertNotNull(qobj);
+
+ assert_checkQProperties(
+ qobj,
+ new ExpectedQProperty("bool", "prop1", Attribute.READ, "getProp"),
+ new ExpectedQProperty("T<bool>", "prop2", Attribute.READ, "getProp"),
+ new ExpectedQProperty("T<bool *>", "prop3", Attribute.READ, "getProp"),
+ new ExpectedQProperty("bool *", "prop4", Attribute.READ, "getProp"),
+ new ExpectedQProperty("bool", "prop5"),
+ new ExpectedQProperty("bool *", "prop6"),
+
+ new ExpectedQProperty("bool", "read1", Attribute.READ, "readMethod"),
+ new ExpectedQProperty("bool", "read2", Attribute.READ ,"readMethod2", Attribute.FINAL),
+
+ new ExpectedQProperty("Namespace::Type", "allowedAreas1" ),
+ new ExpectedQProperty("Qt::ToolBarAreas", "allowedAreas2",
+ Attribute.READ, "allowedAreas",
+ Attribute.WRITE, "setAllowedAreas",
+ Attribute.DESIGNABLE, "(qobject_cast<QMainWindow *>(parentWidget()) != 0)",
+ Attribute.NOTIFY, "allowedAreasChanged")
+ );
+ }
+
+ // #include "junit-QObject.hh"
+ // class B : public QObject
+ // {
+ // Q_OBJECT
+ // Q_PROPERTY(bool allowed READ isAllowed)
+ // public:
+ // bool isAllowed() const { return false; }
+ // };
+ // class D1 : public B
+ // {
+ // Q_OBJECT
+ // Q_PROPERTY(bool allowed READ isAllowed_d)
+ // public:
+ // bool isAllowed_d() const { return false; }
+ // };
+ public void testGetOverridden() throws Exception {
+ loadComment("getOverridden.hh");
+
+ QtIndex qtIndex = QtIndex.getIndex(fProject);
+ assertNotNull(qtIndex);
+
+ IQObject base_qobj = qtIndex.findQObject(new String[]{ "B" });
+ if (!isIndexOk("B", base_qobj))
+ return;
+ assertNotNull(base_qobj);
+
+ IQObject.IMembers<IQProperty> base_qprops = base_qobj.getProperties();
+ assertNotNull(base_qprops);
+ assertEquals(1, base_qprops.all().size());
+ assertEquals(1, base_qprops.locals().size());
+ assertEquals(1, base_qprops.withoutOverrides().size());
+
+ IQObject derived_qobj1 = qtIndex.findQObject(new String[]{ "D1" });
+ assertNotNull(derived_qobj1);
+ IQObject.IMembers<IQProperty> derived_qprops1 = derived_qobj1.getProperties();
+ assertNotNull(derived_qprops1);
+ assertEquals(2, derived_qprops1.all().size());
+ assertEquals(1, derived_qprops1.locals().size());
+ assertEquals(1, derived_qprops1.withoutOverrides().size());
+ }
+
+ // #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"));
+ }
+
+ private static class ExpectedQProperty {
+ public final String type;
+ public final String name;
+ Object[] attributes;
+ public ExpectedQProperty(String type, String name, Object... attributes) {
+ this.type = type;
+ this.name = name;
+ this.attributes = attributes;
+ }
+ }
+
+ /**
+ * A utility method for testing Q_PROPERTYs. The given object is checked for the list of
+ * values. Only the locally declared properties are checked and the list must be complete.
+ */
+ private static void assert_checkQProperties(IQObject qobj, ExpectedQProperty... expectedProperties) throws Exception {
+
+ // this map is used to make sure that all expected attributes are found
+ Map<String, ExpectedQProperty> qprops = new HashMap<String, QObjectTests.ExpectedQProperty>();
+ for(ExpectedQProperty qprop : expectedProperties)
+ if (qprops.containsKey(qprop.name))
+ fail("duplicate properties in expected list " + qprop.name);
+ else
+ qprops.put(qprop.name, qprop);
+
+ for(IQProperty qprop : qobj.getProperties().locals()) {
+ ExpectedQProperty expected = qprops.remove(qprop.getName());
+ assertNotNull("unexpected or duplicate attribute " + qprop.getName(), expected);
+ assertEquals("unexpected type for " + expected.name, expected.type, qprop.getType());
+ assertEquals("unexpected type for " + expected.name, expected.name, qprop.getName());
+
+ // make sure that all attributes that were found were expected
+ Set<Attribute> allAttrs = new HashSet<Attribute>(Arrays.asList(Attribute.values()));
+
+ for(int i = 0; i < expected.attributes.length; ++i) {
+ Attribute attr = (Attribute)expected.attributes[i];
+
+ // make sure the test is valid -- search for each attribute at most once
+ assertTrue(allAttrs.remove(attr));
+
+ if (!attr.hasValue)
+ assertNotNull("missing " + attr.toString(), attr.valueIn(qprop));
+ else if(i >= (expected.attributes.length - 1)
+ || expected.attributes[i + 1] instanceof Attribute)
+ fail("INVALID TEST CASE: " + attr + " should have a value, but one was not provided" );
+ else {
+ Object exp = expected.attributes[++i];
+ assertEquals(attr.toString(), exp, attr.valueIn(qprop));
+ }
+ }
+
+ // make sure there is no value for all other attributes
+ for(Attribute attr : allAttrs)
+ assertTrue("unexpectedly found value for " + attr, attr.valueIn(qprop) == null);
+ }
+
+ // make sure that all expected properties were found
+ StringBuilder missingAttrs = new StringBuilder();
+ for(String propName : qprops.keySet()) {
+ if (missingAttrs.length() > 0)
+ missingAttrs.append(", ");
+ missingAttrs.append(propName);
+ }
+ assertTrue("missing properties " + missingAttrs.toString(), missingAttrs.length() == 0);
+ }
}