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 b4f1de57ab7..276f4dd51a5 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 54678f8807c..17670e26a5e 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 *possible* for this member and the parameter + * to override each the other. A true result indicates that *if* 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 { + /** + * Returns an unmodifiable collection with all locally declared, inherited, and overridden + * members. Does not return null. + */ + public Collection all(); + + /** + * Returns an unmodifiable collection with only the members that are locally declared in the + * source class. Does not return null. + */ + public Collection locals(); + + /** + * Returns an unmodifiable collection of all locally declared and inherited members with the + * overridden members filtered out. Does not return null. + */ + public Collection withoutOverrides(); + } + /** * Returns the name of the class. */ @@ -38,6 +86,11 @@ public interface IQObject extends IQElement { */ public List getBases(); + /** + * Returns the expansions of the Q_PROPERTY macro. Does not return null. + */ + public IMembers 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 00000000000..663ec7b8a5e --- /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: + *
+ * Q_PROPERTY( type name
+ *             READ getFunction
+ *             [WRITE setFunction]
+ *             [RESET resetFunction]
+ *             [NOTIFY notifySignal]
+ *             [REVISION int]
+ *             [DESIGNABLE bool]
+ *             [SCRIPTABLE bool]
+ *             [STORED bool]
+ *             [USER bool]
+ *             [CONSTANT]
+ *             [FINAL] )
+ * 
+ * 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., + *
+		 * IQProperty qprop;
+		 * String readFunction = IQProperty.Attribute.READ.valueId( qprop );
+		 * 
+ * 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 + *
+	 * Q_PROPERTY( bool allowed READ isAllowed )
+	 * 
+	 * 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 00000000000..9d11ebb2621
--- /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 be86d8ad442..9a050d5b638 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 bases;
+	private final IQObject.IMembers properties;
 	private final List enums;
 	private final Map classInfos;
 
@@ -32,18 +34,29 @@ public class QObject implements IQObject {
 		this.name = pdomQObject.getName();
 		this.pdomQObject = pdomQObject;
 
+		List baseProps = new ArrayList();
+
 		this.bases = new ArrayList();
-		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();
-		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 props = new ArrayList();
+		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
@@ -61,6 +74,11 @@ public class QObject implements IQObject {
 		return bases;
 	}
 
+	@Override
+	public IQObject.IMembers getProperties() {
+		return properties;
+	}
+
 	@Override
 	public String getClassInfo(String key) {
 		String value = classInfos.get(key);
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 00000000000..73116d92b2b
--- /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 implements IQObject.IMembers  {
+
+	private final List all;
+	private final Collection locals;
+	private Collection withoutOverrides;
+
+	public static  QObjectMembers create(Collection locals, Collection 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 all = new ArrayList(locals.size() + inherited.size());
+		all.addAll(locals);
+		all.addAll(inherited);
+		return new QObjectMembers(all, locals);
+	}
+
+	private QObjectMembers(List all, Collection locals) {
+		this.all = Collections.unmodifiableList(all);
+		this.locals = Collections.unmodifiableCollection(locals);
+	}
+
+	@Override
+	public Collection all() {
+		return all;
+	}
+
+	@Override
+	public Collection locals() {
+		return locals;
+	}
+
+	@Override
+	public Collection 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 filtered = new ArrayList(all.size());
+					for(T member : all) {
+						boolean isOverridden = false;
+						for(Iterator 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 00000000000..8d0bf81f606
--- /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:
+	 * 
+	 * (:?READ)|(?:WRITE)|(:?RESET)|...
+	 * 
+ * 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 d102a466474..81b66da9fb6 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 00000000000..3ab9467d43f --- /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). + *

+ * 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 findFromBinding(Class cls, PDOMBinding binding) throws CoreException { + if (binding == null) + return null; + + // Look for external references to the binding. + IPDOMIterator 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 00000000000..a9bf082364d --- /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 00000000000..aead1422f2a --- /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 00000000000..040f2a850d4 --- /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 { + /** + * 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 9699c798fd9..d84e11b2338 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 properties = new ArrayList(); private final Map classInfos = new LinkedHashMap(); private IASTNode parent; @@ -40,6 +43,14 @@ public class QObjectName extends ASTDelegatedName { this.propertyInParent = delegate.getPropertyInParent(); } + public List getProperties() { + return properties; + } + + public void addProperty(QtPropertyName property) { + properties.add(property); + } + public Map 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 b8a3b6d6806..6b5256e966b 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 { *

* 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: + *

+	 * (:?READ)|(?:WRITE)|(:?RESET)|...
+	 * 
+ * 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 enumDecls = new ArrayList(); @@ -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 1537f83dbde..00000000000 --- 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 5536f110c57..fa766ffb697 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 00000000000..ea3f6a649a4 --- /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 { + + 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 codec; + private long record; + + public QtPDOMArray(QtPDOMLinkage linkage, IQtPDOMCodec codec, long record) { + this.linkage = linkage; + this.codec = codec; + this.record = record; + } + + public QtPDOMArray(QtPDOMLinkage linkage, IQtPDOMCodec 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 e5548905467..293b797f216 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 1f1e6c4f75c..983de4e8641 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 cache = new HashMap(); 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); + public long getCPPRecord(IASTName cppName) throws CoreException { - 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(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. + *

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

* 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 00000000000..a414295a5e5 --- /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 pdomArray = new QtPDOMArray(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 pdomArray = new QtPDOMArray(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 Codec = new IQtPDOMCodec() { + @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 c28139955d7..753f5f0c257 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 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.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 21222d18068..675b053d27a 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 delete() throws CoreException { + long fieldRec = Field.ClassInfos.getRecord(record); + new QtPDOMArray(getQtLinkage(), ClassInfo.Codec, fieldRec).delete(); + getDB().putRecPtr(Field.ClassInfos.getRecord(record), 0); + } + + public ICPPClassType getCppClassType() 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(ICPPClassType.class); + + return cppBinding instanceof ICPPClassType ? (ICPPClassType) cppBinding : null; } public void setClassInfos(Map classInfos) throws CoreException { - // 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; - // 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); + // Create an array to be stored to the PDOM. + ClassInfo[] array = new ClassInfo[classInfos.size()]; + Iterator> iterator = classInfos.entrySet().iterator(); + for(int i = 0; i < array.length && iterator.hasNext(); ++i) { + Map.Entry entry = iterator.next(); + array[i] = new ClassInfo(entry.getKey(), entry.getValue()); } - // Clear the pointer if the incoming map is empty. - if (classInfos.isEmpty()) { - getDB().putRecPtr(Field.ClassInfos.getRecord(record), 0); - return; - } + // Store the array into the Database. + long arrayRec = getDB().getRecPtr(Field.ClassInfos.getRecord(record)); + QtPDOMArray pdomArray = new QtPDOMArray(getQtLinkage(), ClassInfo.Codec, arrayRec); + arrayRec = pdomArray.set(array); - // Otherwise create a block large enough to hold the incoming list and then populate it. - block = getDB().malloc(Database.INT_SIZE + (classInfos.size() * 2 * Database.PTR_SIZE)); - getDB().putInt(block, classInfos.size()); - - long b = block + Database.INT_SIZE; - for(Map.Entry classInfo : classInfos.entrySet()) { - IString key = getDB().newString(classInfo.getKey()); - IString val = getDB().newString(classInfo.getValue()); - - getDB().putRecPtr(b, key.getRecord()); b += Database.PTR_SIZE; - getDB().putRecPtr(b, val.getRecord()); b += Database.PTR_SIZE; - } - - // Put the new block into the PDOM. - getDB().putRecPtr(Field.ClassInfos.getRecord(record), block); + // Update the record that is stored in the receiver's field. + getDB().putRecPtr(Field.ClassInfos.getRecord(record), arrayRec); } public Map getClassInfos() throws CoreException { Map classInfos = new LinkedHashMap(); - // 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; + // 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 pdomArray = new QtPDOMArray(getQtLinkage(), ClassInfo.Codec, arrayRec); - 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; + ClassInfo[] array = pdomArray.get(); + if (array == null) + return classInfos; - rec = getDB().getRecPtr(block); - IString val = getDB().getString(rec); - block += Database.PTR_SIZE; - - 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 findBases() throws CoreException { - IBinding cppBinding = getCppBinding(); - if (!(cppBinding instanceof ICPPClassType)) + ICPPClassType cppClassType = getCppClassType(); + if (cppClassType == null) return Collections.emptyList(); List bases = new ArrayList(); - 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 List getFields(Class cls) throws CoreException { + QtPDOMVisitor.All collector = new QtPDOMVisitor.All(cls); + try { + children.accept(collector); + } catch(CoreException e) { + QtPlugin.log(e); + return Collections.emptyList(); + } + + return collector.list; + } + @Override public IField[] getFields() { QtPDOMVisitor.All collector = new QtPDOMVisitor.All(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 Codec = new IQtPDOMCodec() { + @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 00000000000..7739e76d4cb --- /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 00000000000..5f3ce6ff806 --- /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 attributes = new ArrayList(); + + 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 9dbde17c3b3..cd5ea297696 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 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 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 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 prop2 READ getProp ) + // Q_PROPERTY( T 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(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", "prop2", Attribute.READ, "getProp"), + new ExpectedQProperty("T", "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(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 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 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 qprops = new HashMap(); + 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 allAttrs = new HashSet(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); + } }