1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-06-07 09:46:02 +02:00

Bug 422841: Add Q_PROPERTY to the QtIndex

This extends IQObject so that it can return the list of Q_PROPERTY
expansions.  Each Q_PROPERTY is represented by the new IQProperty.  The
attributes of the property are stored in IQProperty.Attribute.

Where applicable, the attributes insert a reference from the associated
C++ binding.  This means that the Q_PROPERTY expansion will be included
in the list of references for the C++ binding.

This also simplifies the process for adding new PDOMBindings to the Qt
linkage.  New instances are stored in an implementation of IQtASTName
and able to directly create and return a new QtPDOMBinding.  This avoids
creating three parallel inheritance hierachies (compared to the previous
Qt PDOM implementations).

The patch includes test cases to check handling of Q_PROPERTY
expansions.

Change-Id: I7d256d1a938d24a9eb726c472fb150a02f26eb32
Signed-off-by: Andrew Eidsness <eclipse@jfront.com>
Reviewed-on: https://git.eclipse.org/r/19602
Tested-by: Hudson CI
Reviewed-by: Doug Schaefer <dschaefer@qnx.com>
IP-Clean: Doug Schaefer <dschaefer@qnx.com>
This commit is contained in:
Andrew Eidsness 2013-12-04 08:00:18 -05:00 committed by Doug Schaefer
parent 431dff5671
commit 2d9f3f1028
26 changed files with 2096 additions and 505 deletions

View file

@ -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);
}

View file

@ -16,6 +16,54 @@ import java.util.List;
* @see IQMethod
*/
public interface IQObject extends IQElement {
/**
* The interface to be implemented by elements that can be returned as members of an
* implementation of {@link IQObject}.
*/
public static interface IMember {
/**
* Return the QObject class that declares this member. Does not return null.
*/
public IQObject getOwner();
/**
* Returns true if it is <strong>*possible*</strong> for this member and the parameter
* to override each the other. A true result indicates that <strong>*if*</strong> the members
* owner's are related by inheritance then one will override the other; the implementation
* does not check that there actually is such an inheritance relationship.
*/
public boolean isOverride(IMember member);
}
/**
* A wrapper for unmodifiable collections of members of a class. Accessors provide filtered
* views of the member list.
*
* @see #all()
* @see #locals()
* @see #withoutOverrides()
*/
public static interface IMembers<T extends IMember> {
/**
* Returns an unmodifiable collection with all locally declared, inherited, and overridden
* members. Does not return null.
*/
public Collection<T> all();
/**
* Returns an unmodifiable collection with only the members that are locally declared in the
* source class. Does not return null.
*/
public Collection<T> locals();
/**
* Returns an unmodifiable collection of all locally declared and inherited members with the
* overridden members filtered out. Does not return null.
*/
public Collection<T> withoutOverrides();
}
/**
* Returns the name of the class.
*/
@ -38,6 +86,11 @@ public interface IQObject extends IQElement {
*/
public List<IQObject> getBases();
/**
* Returns the expansions of the Q_PROPERTY macro. Does not return null.
*/
public IMembers<IQProperty> getProperties();
/**
* Examines the Q_CLASSINFO expansions to return the value associated with the given
* key. Returns null if there isn't a Q_CLASSINFO for the given key.

View file

@ -0,0 +1,164 @@
/*
* Copyright (c) 2013 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.qt.core.index;
/**
* A container for things declared as Q_PROPERTY within a subclass of QObject. In Qt 4.8,
* Q_PROPERTY looks like:
* <pre>
* Q_PROPERTY( type name
* READ getFunction
* [WRITE setFunction]
* [RESET resetFunction]
* [NOTIFY notifySignal]
* [REVISION int]
* [DESIGNABLE bool]
* [SCRIPTABLE bool]
* [STORED bool]
* [USER bool]
* [CONSTANT]
* [FINAL] )
* </pre>
* This interface provides structured access to the elements of the Q_PROPERTY expansion.
*/
public interface IQProperty extends IQObject.IMember {
/**
* The attributes that were defined in Qt 4.8. This is the full set of attributes
* that will be parsed from Q_PROPERTY expansions.
*/
public static enum Attribute {
// This enum is used when scanning Q_PROPERTY expansions, only attributes listed here
// will be extracted from the expansion. This enum should be expanded with new values
// as needed (based on the version of Qt). See QProperty#scanAttributes.
READ("getFunction"),
WRITE("setFunction"),
RESET("resetFunction"),
NOTIFY("notifySignal"),
REVISION("int"),
DESIGNABLE("bool"),
SCRIPTABLE("bool"),
STORED("bool"),
USER("bool"),
CONSTANT(null),
FINAL(null);
/**
* A string containing the C++ identifier for this attribute.
*/
public final String identifier = toString();
/**
* Stores the attribute parameter name if it exists and null for attributes
* without parameters. The parameter name comes from the Qt documentation
* and is only intended for documentation.
*/
public final String paramName;
/**
* True when this attribute is expected to have a value and false otherwise.
*/
public final boolean hasValue;
private Attribute(String paramName) {
this.paramName = paramName;
this.hasValue = paramName != null;
}
/**
* A convenience method to access the value of a property through this
* enum, e.g.,
* <pre>
* IQProperty qprop;
* String readFunction = IQProperty.Attribute.READ.valueId( qprop );
* </pre>
* Returns null if there is no associated value in the given property.
*/
public String valueIn(IQProperty qprop) {
return qprop == null ? null : qprop.getValue(this);
}
}
/**
* Returns the type of the property. This is the first field in the Q_PROPERTY expansion.
*/
public String getType();
/**
* Returns the name of the property. This is the second field in the Q_PROPERTY expansion.
*/
public String getName();
/**
* Return the value of the attribute associated with the given key. E.g., in
* <pre>
* Q_PROPERTY( bool allowed READ isAllowed )
* <pre>
* The parameter Attribute.READ would return "isAllowed".
*
* Returns null if the given key is not described in the property.
*/
public String getValue(Attribute attr);
/**
* Return the value of READ or null if READ is not described in the property.
*/
public String getReadMethodName();
/**
* Return the value of WRITE or null if WRITE is not described in the property.
*/
public String getWriteMethodName();
/**
* Return the value of RESET or null if RESET is not described in the property.
*/
public String getResetMethodName();
/**
* Return the value of NOTIFY or null if NOTIFY is not described in the property.
*/
public String getNotifyMethodName();
/**
* Return the value of REVISION or null if REVISION is not described in the property.
* The return type is Long in order to accommodate unsigned C++ 32-bit values.
*/
public Long getRevision();
/**
* Return the value of DESIGNABLE or null if DESIGNABLE is not described in the property.
*/
public String getDesignable();
/**
* Return the value of SCRIPTABLE or null if SCRIPTABLE is not described in the property.
*/
public String getScriptable();
/**
* Return the value of STORED or null if STORED is not described in the property.
*/
public String getStored();
/**
* Return the value of USER or null if USER is not described in the property.
*/
public String getUser();
/**
* Return true if CONSTANT was specified in the Q_PROPERTY expansion and false otherwise.
*/
public boolean isConstant();
/**
* Return true if FINAL was specified in the Q_PROPERTY expansion and false otherwise.
*/
public boolean isFinal();
}

View file

@ -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);
}
}

View file

@ -13,9 +13,10 @@ import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.qt.core.index.IQEnum;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.IQProperty;
import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMProperty;
import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQEnum;
import org.eclipse.cdt.qt.internal.core.pdom.QtPDOMQObject;
import org.eclipse.core.runtime.CoreException;
@ -25,6 +26,7 @@ public class QObject implements IQObject {
private final String name;
private final QtPDOMQObject pdomQObject;
private final List<IQObject> bases;
private final IQObject.IMembers<IQProperty> properties;
private final List<IQEnum> enums;
private final Map<String, String> classInfos;
@ -32,18 +34,29 @@ public class QObject implements IQObject {
this.name = pdomQObject.getName();
this.pdomQObject = pdomQObject;
List<IQProperty> baseProps = new ArrayList<IQProperty>();
this.bases = new ArrayList<IQObject>();
for(QtPDOMQObject base : pdomQObject.findBases())
this.bases.add(new QObject(qtIndex, cdtIndex, base));
for(QtPDOMQObject base : pdomQObject.findBases()) {
QObject baseQObj = new QObject(qtIndex, cdtIndex, base);
this.bases.add(baseQObj);
baseProps.addAll(baseQObj.getProperties().all());
}
this.classInfos = pdomQObject.getClassInfos();
this.enums = new ArrayList<IQEnum>();
for(IField field : pdomQObject.getFields())
if (field instanceof QtPDOMQEnum) {
QtPDOMQEnum qEnum = (QtPDOMQEnum) field;
this.enums.add(new QEnum(field.getName(), qEnum.isFlag(), qEnum.getEnumerators()));
for(QtPDOMQEnum pdom : pdomQObject.getFields(QtPDOMQEnum.class))
this.enums.add(new QEnum(pdom.getName(), pdom.isFlag(), pdom.getEnumerators()));
List<IQProperty> props = new ArrayList<IQProperty>();
for(QtPDOMProperty pdom : pdomQObject.getFields(QtPDOMProperty.class)) {
QProperty qProp = new QProperty(this, pdom.getTypeStr(), pdom.getName());
for(QtPDOMProperty.Attribute attr : pdom.getAttributes())
qProp.setAttribute(attr.attr, attr.value);
props.add(qProp);
}
this.properties = QObjectMembers.create(props, baseProps);
}
@Override
@ -61,6 +74,11 @@ public class QObject implements IQObject {
return bases;
}
@Override
public IQObject.IMembers<IQProperty> getProperties() {
return properties;
}
@Override
public String getClassInfo(String key) {
String value = classInfos.get(key);

View file

@ -0,0 +1,86 @@
/*
* Copyright (c) 2013 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.qt.internal.core.index;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.cdt.qt.core.index.IQObject;
public class QObjectMembers<T extends IQObject.IMember> implements IQObject.IMembers<T> {
private final List<T> all;
private final Collection<T> locals;
private Collection<T> withoutOverrides;
public static <T extends IQObject.IMember> QObjectMembers<T> create(Collection<T> locals, Collection<T> inherited) {
// NOTE: All must be ordered with the locals before the inherited members. This ensures that
// the algorithm for computing #withoutOverrides will filter out the parent members and
// not the local ones.
// @see withoutOverrides()
ArrayList<T> all = new ArrayList<T>(locals.size() + inherited.size());
all.addAll(locals);
all.addAll(inherited);
return new QObjectMembers<T>(all, locals);
}
private QObjectMembers(List<T> all, Collection<T> locals) {
this.all = Collections.unmodifiableList(all);
this.locals = Collections.unmodifiableCollection(locals);
}
@Override
public Collection<T> all() {
return all;
}
@Override
public Collection<T> locals() {
return locals;
}
@Override
public Collection<T> withoutOverrides() {
if (withoutOverrides == null)
synchronized (all) {
if (withoutOverrides == null) {
// Naively tests each existing element for override before inserting the new
// element. Most member lists have less than 3 elements, and the largest that
// I've found (in the Qt impl) is about 20; so performance may not be as bad
// as it seems.
//
// An earlier approach tried to use a SortedSet with the #isOverride result in
// the Comparator. The problem with the approach is finding a stable sort order
// when the members don't override each other.
// E.g., if o1 and o2 override each other and m is unrelated, we could get a
// tree like:
// m
// / \
// o1 o2
ArrayList<T> filtered = new ArrayList<T>(all.size());
for(T member : all) {
boolean isOverridden = false;
for(Iterator<T> i = filtered.iterator(); !isOverridden && i.hasNext(); )
isOverridden = member.isOverride(i.next());
if (!isOverridden)
filtered.add(member);
}
withoutOverrides = Collections.unmodifiableCollection(filtered);
}
}
return withoutOverrides;
}
}

View file

@ -0,0 +1,213 @@
/*
* Copyright (c) 2013 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.qt.internal.core.index;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.IQProperty;
public class QProperty extends AbstractQField implements IQProperty {
private String type;
private final String[] values = new String[Attribute.values().length];
public QProperty(IQObject owner, String type, String name) {
super(owner);
this.type = type;
this.name = name;
}
/**
* A regular expression for scanning the full Q_PROPERTY expansion and extracting the
* expansion parameter. It provides the following capture groups:
* 1 - the type
* 2 - the name
* 3 - the trimmed remainder of the expansion parameter (starting with READ)
*
* This REGEX handles cases like:
* Q_PROPERTY(T* t READ ... )
* Q_PROPERTY(T * t READ ... )
* Q_PROPERTY(T *t READ ... )
* This REGEX assumes that READ will directly follow the property name. This is implied,
* although not explicitly stated in the Qt documentation.
*
* It also allows the option of having no other attribute (just type and name). The Qt
* documentation forbids this, but it is used in QtSensors/
*/
private static final Pattern EXPANSION_REGEX = Pattern.compile("^(.+?)\\s*([a-zA-Z_][\\w]*+)(?:(?:\\s+(READ\\s+.*))|\\s*)$");
/**
* A regular expression for scanning Q_PROPERTY attributes. The regular expression is built
* from the values defined in IQProperty#Attribute. It looks like:
* <pre>
* (:?READ)|(?:WRITE)|(:?RESET)|...
* </pre>
* This regular expression is used to recognize valid attributes while scanning the
* Q_PROPERTY macro expansion.
*
* @see QProperty#scanAttributes(String)
*/
private static final Pattern ATTRIBUTE_REGEX;
static {
StringBuilder regexBuilder = new StringBuilder();
for(IQProperty.Attribute attr : IQProperty.Attribute.values()) {
if (attr.ordinal() > 0)
regexBuilder.append('|');
regexBuilder.append("(:?");
regexBuilder.append(attr.identifier);
regexBuilder.append(")");
}
ATTRIBUTE_REGEX = Pattern.compile(regexBuilder.toString());
}
/**
* Scans the given field and extracts the strings defining the attributes of the
* Q_PROPERTY. Returns false if the field is does not represent a Q_PROPERTY, does
* not have attribute-related information, or if the information does not match the
* expected format.
* @param field
* @return
*/
@Override
protected boolean scanDefn(String expansionParam) {
Matcher m = EXPANSION_REGEX.matcher(expansionParam);
if (!m.matches())
return false;
this.type = m.group(1);
this.name = m.group(2);
return scanAttributes(m.group(3));
}
/**
* Scans the given string to extract values for all recognized attributes. A regular expression
* is used to find the attributes, substrings between attributes are assigned as values.
* Attributes that don't expect a value (as determined by {@link IQProperty#Attribute#hasValue}),
* as assigned "true".
*/
private boolean scanAttributes(String attributes) {
if (attributes == null)
return true;
int lastEnd = 0;
IQProperty.Attribute lastAttr = null;
for(Matcher attributeMatcher = ATTRIBUTE_REGEX.matcher(attributes); attributeMatcher.find(); lastEnd = attributeMatcher.end()) {
// set the value of attribute found in the previous iteration to the substring between
// the end of that attribute and the start of this one
if (lastAttr != null) {
String value = attributes.substring(lastEnd, attributeMatcher.start());
values[lastAttr.ordinal()] = value.trim();
}
// the regex is built from the definition of the enum, so none of the strings that it
// finds will throw an exception
lastAttr = IQProperty.Attribute.valueOf(IQProperty.Attribute.class, attributeMatcher.group(0));
// if this attribute doesn't have a value, then put it into the value map immediately
// and make sure it is not used later in this scan
if (!lastAttr.hasValue) {
values[lastAttr.ordinal()] = Boolean.TRUE.toString();
lastAttr = null;
}
}
// the value of the last attribute in the expansion is the substring between the end of
// the attribute identifier and the end of the string
if (lastAttr != null) {
String value = attributes.substring(lastEnd);
values[lastAttr.ordinal()] = value.trim();
}
return true;
}
public void setAttribute(IQProperty.Attribute attr, String value) {
values[attr.ordinal()] = ( value == null ? "" : value );
}
@Override
public String getType() {
return type;
}
@Override
public String getName() {
return name;
}
@Override
public String getValue(Attribute attr) {
return values[attr.ordinal()];
}
@Override
public String getReadMethodName() {
return Attribute.READ.valueIn(this);
}
@Override
public String getWriteMethodName() {
return Attribute.WRITE.valueIn(this);
}
@Override
public String getResetMethodName() {
return Attribute.RESET.valueIn(this);
}
@Override
public String getNotifyMethodName() {
return Attribute.NOTIFY.valueIn(this);
}
@Override
public Long getRevision() {
String revision = Attribute.REVISION.valueIn(this);
if (revision != null)
try {
return Long.valueOf(revision);
} catch(NumberFormatException e) {
// This is a problem with the user's C++ code, there is no need to log this exception,
// just ignore the value.
}
return null;
}
@Override
public String getDesignable() {
return Attribute.DESIGNABLE.valueIn(this);
}
@Override
public String getScriptable() {
return Attribute.SCRIPTABLE.valueIn(this);
}
@Override
public String getStored() {
return Attribute.STORED.valueIn(this);
}
@Override
public String getUser() {
return Attribute.USER.valueIn(this);
}
@Override
public boolean isConstant() {
return Attribute.CONSTANT.valueIn(this) != null;
}
@Override
public boolean isFinal() {
return Attribute.FINAL.valueIn(this) != null;
}
}

View file

@ -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

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2013 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.qt.internal.core.pdom;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNameOwner;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.internal.core.index.IIndexFragmentBinding;
import org.eclipse.cdt.internal.core.index.IIndexFragmentName;
import org.eclipse.cdt.internal.core.pdom.dom.IPDOMIterator;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMName;
import org.eclipse.core.runtime.CoreException;
/**
* Some Qt elements are introduced with empty macro expansions. The Qt linkage handles this
* by creating a new name and then adding it as a reference to the C++ language element.
* This utility helps by containing that C++ name and the location of the Qt name.
*/
@SuppressWarnings("restriction")
public class ASTNameReference extends ASTDelegatedName {
private final IASTFileLocation location;
/**
* Create and return a name that will reference the given name.
*/
public ASTNameReference(IASTName name) {
super(name);
this.location = name.getFileLocation();
}
/**
* Create and return a name that will reference the given name from the given location.
*/
public ASTNameReference(IASTName name, IASTFileLocation location) {
super(name);
this.location = location;
}
/**
* Find and return the Qt binding that annotates the given PDOMBinding. E.g., if the input binding
* is an instance of PDOMCPPClassType, then this method will return the QtPDOMQObject that was created
* from that class (or null if there is no such Qt element).
* <p>
* This is implemented by creating an ASTNameReference within the Qt element binding's definition. That
* name is added as reference from the C++ PDOM binding.
*/
public static <T extends QtPDOMBinding> T findFromBinding(Class<T> cls, PDOMBinding binding) throws CoreException {
if (binding == null)
return null;
// Look for external references to the binding.
IPDOMIterator<PDOMName> pdomIterator = binding.getExternalReferences();
while(pdomIterator.hasNext()) {
PDOMName extRef = pdomIterator.next();
IIndexName caller = extRef.getEnclosingDefinition();
if (caller instanceof IIndexFragmentName) {
IIndexFragmentBinding extRefBinding = ((IIndexFragmentName) caller).getBinding();
if (cls.isAssignableFrom(extRefBinding.getClass()))
return cls.cast(extRefBinding);
}
}
return null;
}
@Override
public IBinding resolveBinding() {
return delegate.resolveBinding();
}
@Override
public IBinding getBinding() {
return delegate.getBinding();
}
@Override
public IASTFileLocation getFileLocation() {
return location;
}
@Override
public boolean isReference() {
return true;
}
@Override
public boolean isDefinition() {
return false;
}
@Override
public boolean isDeclaration() {
return false;
}
@Override
public int getRoleOfName(boolean allowResolution) {
return IASTNameOwner.r_reference;
}
}

View file

@ -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$
}
}

View file

@ -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;
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2013 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.qt.internal.core.pdom;
import org.eclipse.core.runtime.CoreException;
/**
* A utility interface for encoding and decoding fixed-sized elements to and
* from the Database.
*/
public interface IQtPDOMCodec<T> {
/**
* Return the number of bytes needed to store a single element.
*/
public int getElementSize();
/**
* Allocate and return a new array to hold the specified number of elements.
*/
public T[] allocArray(int count);
/**
* Examine the database at the specified record to decode an element instance.
*/
public T decode(QtPDOMLinkage linkage, long record) throws CoreException;
/**
* Encode the given element into the database at the specified record. The codec is
* responsible for releasing storage that is about to be overwritten (if needed).
* The element will be null when the implementation should delete all memory used
* for storage at record.
*/
public void encode(QtPDOMLinkage linkage, long record, T element) throws CoreException;
}

View file

@ -7,7 +7,9 @@
*/
package org.eclipse.cdt.qt.internal.core.pdom;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.cdt.core.dom.ILinkage;
@ -16,18 +18,19 @@ import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.internal.core.dom.Linkage;
import org.eclipse.core.runtime.CoreException;
/**
* QObjects are C++ classes that have been annotated with Qt marker macros. This class is
* used to introduce the QObject to the Qt linkage.
*/
@SuppressWarnings("restriction")
public class QObjectName extends ASTDelegatedName {
public class QObjectName extends ASTDelegatedName implements IQtASTName {
private final ICPPASTCompositeTypeSpecifier spec;
private final List<QtPropertyName> properties = new ArrayList<QtPropertyName>();
private final Map<String, String> classInfos = new LinkedHashMap<String, String>();
private IASTNode parent;
@ -40,6 +43,14 @@ public class QObjectName extends ASTDelegatedName {
this.propertyInParent = delegate.getPropertyInParent();
}
public List<QtPropertyName> getProperties() {
return properties;
}
public void addProperty(QtPropertyName property) {
properties.add(property);
}
public Map<String, String> getClassInfos() {
return classInfos;
}
@ -49,8 +60,8 @@ public class QObjectName extends ASTDelegatedName {
}
@Override
protected IBinding createBinding() {
return new QtBinding(QtPDOMNodeType.QObject, this, spec.getName());
public QtPDOMBinding createPDOMBinding(QtPDOMLinkage linkage) throws CoreException {
return new QtPDOMQObject(linkage, this, spec.getName());
}
@Override

View file

@ -15,7 +15,9 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.cdt.core.dom.ast.ASTVisitor;
import org.eclipse.cdt.core.dom.ast.IASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTFileLocation;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
@ -23,12 +25,15 @@ import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIncludeStatement;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition;
import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroExpansion;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.IScope;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTCompositeTypeSpecifier;
import org.eclipse.cdt.core.index.IIndexSymbols;
import org.eclipse.cdt.internal.core.dom.parser.cpp.ICPPInternalBinding;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPSemantics;
import org.eclipse.cdt.internal.core.parser.scanner.LocationMap;
import org.eclipse.cdt.qt.core.QtKeywords;
import org.eclipse.cdt.qt.core.index.IQProperty;
import org.eclipse.cdt.qt.internal.core.index.QProperty;
@SuppressWarnings("restriction")
public class QtASTVisitor extends ASTVisitor {
@ -36,10 +41,10 @@ public class QtASTVisitor extends ASTVisitor {
private final IIndexSymbols symbols;
private final LocationMap locationMap;
private static final Pattern expansionParamRegex = Pattern.compile("^" + "(?:Q_ENUMS|Q_FLAGS)" + "\\s*\\((.*)\\)$");
private static final Pattern expansionParamRegex = Pattern.compile("^(?:Q_ENUMS|Q_FLAGS)\\s*\\((.*)\\)$", Pattern.DOTALL);
private static final Pattern qualNameRegex = Pattern.compile("\\s*((?:[^\\s:]+\\s*::\\s*)*[^\\s:]+).*");
private static final Pattern declareFlagsRegex = Pattern.compile("^Q_DECLARE_FLAGS\\s*\\(\\s*([^\\s]+),\\s*([^\\s]+)\\s*\\)$");
private static final Pattern declareFlagsRegex = Pattern.compile("^Q_DECLARE_FLAGS\\s*\\(\\s*([^\\s]+),\\s*([^\\s]+)\\s*\\)$", Pattern.DOTALL);
/**
* A regular expression for scanning the Q_CLASSINFO expansion and extracting the
@ -49,7 +54,35 @@ public class QtASTVisitor extends ASTVisitor {
* <p>
* The key must not have embedded quotes.
*/
private static final Pattern classInfoRegex = Pattern.compile("^Q_CLASSINFO\\s*\\(\\s*\"([^\"]+)\"\\s*,\\s*\"(.*)\"\\s*\\)$");
private static final Pattern classInfoRegex = Pattern.compile("^Q_CLASSINFO\\s*\\(\\s*\"([^\"]+)\"\\s*,\\s*\"(.*)\"\\s*\\)$", Pattern.DOTALL);
private static final Pattern leadingWhitespaceRegex = Pattern.compile("^\\s*([^\\s].*)$");
private static final Pattern qPropertyRegex = Pattern.compile("^Q_PROPERTY\\s*\\(\\s*(.+?)\\s*([a-zA-Z_][\\w]*+)(?:(?:\\s+(READ\\s+.*))|\\s*)\\s*\\)$", Pattern.DOTALL);
/**
* A regular expression for scanning Q_PROPERTY attributes. The regular expression is built
* from the values defined in IQProperty#Attribute. It looks like:
* <pre>
* (:?READ)|(?:WRITE)|(:?RESET)|...
* </pre>
* This regular expression is used to recognize valid attributes while scanning the
* Q_PROPERTY macro expansion.
*
* @see QProperty#scanAttributes(String)
*/
private static final Pattern qPropertyAttributeRegex;
static {
StringBuilder regexBuilder = new StringBuilder();
for(IQProperty.Attribute attr : IQProperty.Attribute.values()) {
if (attr.ordinal() > 0)
regexBuilder.append('|');
regexBuilder.append("(:?");
regexBuilder.append(attr.identifier);
regexBuilder.append(")");
}
qPropertyAttributeRegex = Pattern.compile(regexBuilder.toString());
}
public QtASTVisitor(IIndexSymbols symbols, LocationMap locationMap) {
shouldVisitDeclSpecifiers = true;
@ -117,7 +150,7 @@ public class QtASTVisitor extends ASTVisitor {
symbols.add(owner, astName, qobjName);
if (cppName != null)
symbols.add(owner, new ASTDelegatedName.Reference(cppName, location), astName);
symbols.add(owner, new ASTNameReference(cppName, location), astName);
}
}
}
@ -128,9 +161,12 @@ public class QtASTVisitor extends ASTVisitor {
QObjectName qobjName = new QObjectName(spec);
symbols.add(owner, qobjName, null);
// The QObject contains a reference to the C++ class that it annotates.
symbols.add(owner, new ASTNameReference(spec.getName()), qobjName);
// There are three macros that are significant to QEnums, Q_ENUMS, Q_FLAGS, and Q_DECLARE_FLAGS.
// All macro expansions in the QObject class definition are examined to find instances of these
// three. Two lists are created during this processing. Then the those lists are uses to create
// three. Two lists are created during this processing. Then those lists are uses to create
// the QEnum instances.
List<EnumDecl> enumDecls = new ArrayList<QtASTVisitor.EnumDecl>();
@ -140,6 +176,7 @@ public class QtASTVisitor extends ASTVisitor {
String macroName = String.valueOf(expansion.getMacroReference());
if (QtKeywords.Q_OBJECT.equals(macroName))
continue;
if (QtKeywords.Q_ENUMS.equals(macroName))
extractEnumDecls(expansion, false, enumDecls);
else if (QtKeywords.Q_FLAGS.equals(macroName))
@ -158,7 +195,8 @@ public class QtASTVisitor extends ASTVisitor {
String value = m.group(2);
qobjName.addClassInfo(key, value);
}
}
} else if(QtKeywords.Q_PROPERTY.equals(macroName))
handleQPropertyDefn(owner, qobjName, expansion);
}
for(EnumDecl decl : enumDecls)
@ -185,4 +223,167 @@ public class QtASTVisitor extends ASTVisitor {
decls.add(new EnumDecl(enumName, isFlag, refName, offset + start, end - start));
}
}
private void handleQPropertyDefn(IASTPreprocessorIncludeStatement owner, QObjectName qobjName, IASTPreprocessorMacroExpansion expansion) {
Matcher m = qPropertyRegex.matcher(expansion.getRawSignature());
if (!m.matches())
return;
String type = m.group(1);
String name = m.group(2);
int nameStart = m.start(2);
int nameEnd = m.end(2);
IASTName refName = expansion.getMacroReference();
QtASTImageLocation location = new QtASTImageLocation(refName.getFileLocation(), nameStart, nameEnd - nameStart);
QtPropertyName propertyName = new QtPropertyName(qobjName, refName, name, location);
propertyName.setType(type);
qobjName.addProperty(propertyName);
symbols.add(owner, propertyName, qobjName);
// Create nodes for all the attributes.
AttrValue[] values = new AttrValue[IQProperty.Attribute.values().length];
String attributes = m.group(3);
if (attributes == null)
return;
int attrOffset = m.start(3);
int lastEnd = 0;
IQProperty.Attribute lastAttr = null;
for(Matcher attributeMatcher = qPropertyAttributeRegex.matcher(attributes); attributeMatcher.find(); lastEnd = attributeMatcher.end()) {
// set the value of attribute found in the previous iteration to the substring between
// the end of that attribute and the start of this one
if (lastAttr != null) {
String value = attributes.substring(lastEnd, attributeMatcher.start());
int wsOffset = 0;
Matcher ws = leadingWhitespaceRegex.matcher(value);
if (ws.matches()) {
value = ws.group(1);
wsOffset = ws.start(1);
}
values[lastAttr.ordinal()] = new AttrValue(attrOffset + lastEnd + wsOffset, value.trim());
}
// the regex is built from the definition of the enum, so none of the strings that it
// finds will throw an exception
lastAttr = IQProperty.Attribute.valueOf(IQProperty.Attribute.class, attributeMatcher.group(0));
// if this attribute doesn't have a value, then put it into the value map immediately
// and make sure it is not used later in this scan
if (!lastAttr.hasValue) {
values[lastAttr.ordinal()] = AttrValue.None;
lastAttr = null;
}
}
// the value of the last attribute in the expansion is the substring between the end of
// the attribute identifier and the end of the string
if (lastAttr != null) {
String value = attributes.substring(lastEnd);
int wsOffset = 0;
Matcher ws = leadingWhitespaceRegex.matcher(value);
if (ws.matches()) {
value = ws.group(1);
wsOffset = ws.start(1);
}
values[lastAttr.ordinal()] = new AttrValue(attrOffset + lastEnd + wsOffset, value.trim());
}
// Put all values into the property name.
for(int i = 0; i < values.length; ++i) {
IQProperty.Attribute attr = IQProperty.Attribute.values()[i];
AttrValue value = values[i];
if (value == null)
continue;
// If the attribute is not expected to have a C++ binding as the value, then it can
// be immediately added to the Q_PROPERTY.
if (!couldHaveBinding(attr)) {
propertyName.addAttribute(attr, value.value);
continue;
}
// Otherwise see if one or more bindings can be found for the value of the attribute.
// TODO Check whether the Qt moc allows for inherited methods.
IBinding[] bindings = null;
IASTNode specNode = qobjName.getParent();
if (specNode instanceof IASTCompositeTypeSpecifier) {
IScope scope = ((IASTCompositeTypeSpecifier) specNode).getScope();
bindings = CPPSemantics.findBindings(scope, value.value, false);
}
// If no bindings are found, then the attribute can be immediately added to the Q_PROPERTY.
if (bindings == null || bindings.length <= 0) {
propertyName.addAttribute(attr, value.value);
continue;
}
// Otherwise create a new attribute for each binding.
for(IBinding foundBinding : bindings) {
propertyName.addAttribute(attr, value.value, foundBinding);
IASTName cppName = findASTName(foundBinding);
if (cppName != null) {
QtASTImageLocation attrLoc = new QtASTImageLocation(refName.getFileLocation(), value.offset, value.value.length());
symbols.add(owner, new ASTNameReference(cppName, attrLoc), propertyName);
}
}
}
}
private static boolean couldHaveBinding(IQProperty.Attribute attr) {
switch(attr) {
case READ:
case WRITE:
case RESET:
case NOTIFY:
case DESIGNABLE:
case SCRIPTABLE:
return true;
case REVISION:
case STORED:
case USER:
case CONSTANT:
case FINAL:
default:
return false;
}
}
private static IASTName findASTName(IBinding binding) {
IASTNode node = null;
if (binding instanceof ICPPInternalBinding) {
node = ((ICPPInternalBinding) binding).getDefinition();
if (node == null)
node = ((ICPPInternalBinding) binding).getDeclarations()[0];
}
if (node == null)
return null;
IASTName astName = node instanceof IASTName ? (IASTName) node : null;
if (astName != null)
return astName;
if (node instanceof IASTDeclarator)
return ((IASTDeclarator) node).getName();
return null;
}
private static class AttrValue {
public final int offset;
public final String value;
public AttrValue(int offset, String value) {
this.offset = offset;
this.value = value;
}
public static final AttrValue None = new AttrValue(0, null);
}
}

View file

@ -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;
}
}

View file

@ -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);
}
}

View file

@ -0,0 +1,127 @@
/*
* Copyright (c) 2013 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.qt.internal.core.pdom;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.core.runtime.CoreException;
/**
* A utility class for storing a fixed-size list of fixed-size elements into the Database.
*/
@SuppressWarnings("restriction")
public class QtPDOMArray<T> {
private static int offsetInitializer = 0;
protected static enum Field {
Count(Database.INT_SIZE),
Data(0); // The size of the block is provided at runtime.
public final int offset;
private Field(int sizeof) {
this.offset = offsetInitializer;
offsetInitializer += sizeof;
}
public long getRecord(long baseRec) {
return baseRec + offset;
}
}
private final QtPDOMLinkage linkage;
private final IQtPDOMCodec<T> codec;
private long record;
public QtPDOMArray(QtPDOMLinkage linkage, IQtPDOMCodec<T> codec, long record) {
this.linkage = linkage;
this.codec = codec;
this.record = record;
}
public QtPDOMArray(QtPDOMLinkage linkage, IQtPDOMCodec<T> codec, T[] array) throws CoreException {
this.linkage = linkage;
this.codec = codec;
this.record = 0;
set(array);
}
public long getRecord() {
return record;
}
/**
* Return array that is stored in the database. Return null if the receiver is not initialized.
*/
public T[] get() throws CoreException {
if (record == 0)
return null;
int count = linkage.getDB().getInt(Field.Count.getRecord(record));
long elementRec = Field.Data.getRecord(record);
T[] array = codec.allocArray(count);
for(int i = 0; i < count; ++i, elementRec += codec.getElementSize())
array[i] = codec.decode(linkage, elementRec);
return array;
}
/**
* Store the given array into the database. This may change the storage location of the
* receiver.
* @param array The array of elements that should be stored. Setting the value to null is
* equivalent to calling {@link #delete()}, which clears all storage.
*/
public long set(T[] array) throws CoreException {
if (array == null)
return delete();
// Initialize the receiver if needed.
if (record == 0) {
record = linkage.getDB().malloc(Field.Data.offset + (array.length * codec.getElementSize()));
linkage.getDB().putInt(Field.Count.getRecord(record), array.length);
} else {
// Check if the storage block needs to be resized.
int count = linkage.getDB().getInt(Field.Count.getRecord(record));
if (count != array.length) {
linkage.getDB().free(record);
record = linkage.getDB().malloc(Field.Data.offset + (array.length * codec.getElementSize()));
linkage.getDB().putInt(Field.Count.getRecord(record), array.length);
}
}
// Write the new content into the database.
long elementRec = Field.Data.getRecord(record);
for(int i = 0; i < array.length; ++i, elementRec += codec.getElementSize())
codec.encode(linkage, elementRec, array[i]);
return record;
}
/**
* Release all storage used by the receiver and its elements.
*/
public long delete() throws CoreException {
// If the receiver is already empty, then there is nothing else to do.
if (record == 0)
return record;
// Otherwise get the size of the stored array and delete each element.
int count = linkage.getDB().getInt(Field.Count.getRecord(record));
if (count > 0) {
long elementRec = Field.Data.getRecord(record);
for(int i = 0; i < count; ++i, elementRec += codec.getElementSize())
codec.encode(linkage, elementRec, null);
}
// Finally, release the entire record.
linkage.getDB().free(record);
record = 0;
return record;
}
}

View file

@ -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) {

View file

@ -7,6 +7,9 @@
*/
package org.eclipse.cdt.qt.internal.core.pdom;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
@ -14,10 +17,8 @@ import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.internal.core.dom.parser.ISerializableEvaluation;
import org.eclipse.cdt.internal.core.dom.parser.ITypeMarshalBuffer;
import org.eclipse.cdt.internal.core.pdom.PDOM;
import org.eclipse.cdt.internal.core.pdom.db.BTree;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.cdt.internal.core.pdom.db.IBTreeComparator;
import org.eclipse.cdt.internal.core.pdom.db.IBTreeVisitor;
import org.eclipse.cdt.internal.core.pdom.dom.FindBinding;
import org.eclipse.cdt.internal.core.pdom.dom.IPDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
@ -32,7 +33,6 @@ public class QtPDOMLinkage extends PDOMLinkage {
private static int offsetInitializer = PDOMLinkage.RECORD_SIZE;
private static enum Field {
Version(Database.INT_SIZE),
CppIndex(Database.PTR_SIZE),
Last(0);
private final int offset;
@ -50,16 +50,12 @@ public class QtPDOMLinkage extends PDOMLinkage {
// The version that has been read from/written to the persisted file.
private int version;
// An index of C++ -> Qt Bindings. This is used for fast lookup of things like the QObject
// for a C++ class in the base specifier list. Each entry in the index is a pair like
// { CPP_Record, Qt_Record }, the CPP_Record is used for comparison when searching the index.
private final BTree cppIndex;
private final Map<IQtASTName, PDOMBinding> cache = new HashMap<IQtASTName, PDOMBinding>();
public QtPDOMLinkage(PDOM pdom, long record) throws CoreException {
super(pdom, record);
version = pdom.getDB().getInt(Field.Version.getRecord(record));
cppIndex = new BTree(pdom.getDB(), Field.CppIndex.getRecord(record), new CppRecordIndexComparator());
}
protected QtPDOMLinkage(PDOM pdom) throws CoreException {
@ -68,11 +64,6 @@ public class QtPDOMLinkage extends PDOMLinkage {
// Initialize the version with whatever is current.
version = QtPDOMNodeType.VERSION;
pdom.getDB().putInt(Field.Version.getRecord(record), version);
long cppIndexRec = Field.CppIndex.getRecord(record);
Database db = pdom.getDB();
db.putRecPtr(cppIndexRec, 0);
cppIndex = new BTree(db, cppIndexRec, new CppRecordIndexComparator());
}
public int getVersion() {
@ -89,16 +80,6 @@ public class QtPDOMLinkage extends PDOMLinkage {
return ILinkage.QT_LINKAGE_ID;
}
public QtPDOMBinding findFromCppRecord(long cppRec) throws CoreException {
CppRecordIndexFinder finder = new CppRecordIndexFinder(cppRec);
cppIndex.accept(finder);
if (finder.foundQtRec == null)
return null;
PDOMNode node = getNode(finder.foundQtRec.longValue());
return node instanceof QtPDOMBinding ? (QtPDOMBinding) node : null;
}
@Override
public PDOMNode getNode(long record, int nodeType) throws CoreException {
return QtPDOMNodeType.load(this, nodeType, record);
@ -123,18 +104,6 @@ public class QtPDOMLinkage extends PDOMLinkage {
&& pdomBinding.getLinkage() == this)
return pdomBinding;
// Otherwise try to create a new PDOMBinding.
QtBinding qtBinding = (QtBinding) binding.getAdapter(QtBinding.class);
if (qtBinding != null)
switch(qtBinding.getType()) {
case QObject:
pdomBinding = new QtPDOMQObject(this, qtBinding);
break;
case QEnum:
pdomBinding = new QtPDOMQEnum(this, adaptBinding(qtBinding.getOwner()), qtBinding);
break;
}
// If a PDOMBinding was created, then add it to the linkage before returning it.
if (pdomBinding != null) {
addChild(pdomBinding);
@ -145,19 +114,8 @@ public class QtPDOMLinkage extends PDOMLinkage {
return getPDOM().getLinkage(ILinkage.CPP_LINKAGE_ID).adaptBinding(binding);
}
public void addChild(QtPDOMBinding child) throws CoreException {
super.addChild(child);
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.
* <p>
* This method is not thread-safe.
*/
public PDOMBinding getBinding(IQtASTName qtAstName) throws CoreException {
// The Qt implementation ensures uniqueness by creating only a single instance of
// the IASTName for each thing that should create a single instance in the PDOM.
// This will work as long as all Qt elements are updated at once, which is currently
// the case.
//
// I don't think this needs to be thread-safe, because things are only added from
// the single indexer task.
PDOMBinding pdomBinding = null;
pdomBinding = cache.get(qtAstName);
if (pdomBinding != null)
return pdomBinding;
// The result is cached even when null is returned.
pdomBinding = qtAstName.createPDOMBinding(this);
cache.put(qtAstName, pdomBinding);
// Only add children that are actually created.
if (pdomBinding != null)
addChild(pdomBinding);
return pdomBinding;
}
@Override
public PDOMBinding addBinding(IASTName name) throws CoreException {
return name == null ? null : adaptBinding(name.getBinding());
// The Qt linkage is able to reference elements in other linkages. This implementation
// needs to decide if the binding associated with this name is from the Qt linkage or
// from one of those external references.
if (name == null)
return null;
if (name instanceof IQtASTName)
return getBinding((IQtASTName) name);
IBinding binding = name.getBinding();
if (binding == null)
return null;
// Use the receiving linkage by default, and override only if the binding is found to
// have a linkage with a different id.
PDOMLinkage pdomLinkage = this;
ILinkage linkage = binding.getLinkage();
if (linkage != null
&& linkage.getLinkageID() != getLinkageID())
pdomLinkage = getPDOM().getLinkage(linkage.getLinkageID());
// Handle bindings in unknown linkages as though the name is to be added to this linkage.
return (pdomLinkage == null ? this : pdomLinkage).adaptBinding(binding);
}
@Override
public int getBindingType(IBinding binding) {
return binding instanceof QtBinding ? ((QtBinding) binding).getType().Type : 0;
return binding instanceof QtPDOMBinding ? ((QtPDOMBinding) binding).getNodeType() : 0;
}
@Override
@ -205,44 +218,4 @@ public class QtPDOMLinkage extends PDOMLinkage {
public ISerializableEvaluation unmarshalEvaluation(ITypeMarshalBuffer typeMarshalBuffer) throws CoreException {
throw new CoreException(QtPlugin.error("Qt Linkage does not marshal evaluations")); //$NON-NLS-1$
}
private class CppRecordIndexComparator implements IBTreeComparator {
@Override
public int compare(long record1, long record2) throws CoreException {
Database db = getDB();
Long cppRec1 = Long.valueOf(db.getRecPtr(record1));
long cppRec2 = Long.valueOf(db.getRecPtr(record2));
return cppRec1.compareTo(cppRec2);
}
}
private class CppRecordIndexFinder extends CppRecordIndexComparator implements IBTreeVisitor {
private final Long targetCppRec;
public Long foundQtRec;
public CppRecordIndexFinder(long targetCppRec) {
this.targetCppRec = Long.valueOf(targetCppRec);
}
@Override
public int compare(long record) throws CoreException {
Long cppRec = Long.valueOf(getDB().getRecPtr(record));
return cppRec.compareTo(targetCppRec);
}
@Override
public boolean visit(long record) throws CoreException {
// Stop searching after the record is found.
if (foundQtRec != null)
return false;
// The record is the pair, so the Qt rec is the second element.
long qtRec = getDB().getRecPtr(record + Database.PTR_SIZE);
foundQtRec = Long.valueOf(qtRec);
return false;
}
}
}

View file

@ -15,7 +15,9 @@ import org.eclipse.core.runtime.CoreException;
public enum QtPDOMNodeType {
QObject,
QEnum;
QEnum,
QProperty,
QPropertyAttribute;
public final int Type = IIndexBindingConstants.LAST_CONSTANT + 1 + ordinal();
@ -26,7 +28,7 @@ public enum QtPDOMNodeType {
* <p>
* The version is needed because ordinals for these enumerators are written to the file.
*/
public static final int VERSION = 2;
public static final int VERSION = 3;
public static QtPDOMNodeType forType(int version, int type) {
// Nothing has been deleted or replaced yet, so the version is ignored.
@ -48,6 +50,8 @@ public enum QtPDOMNodeType {
return new QtPDOMQObject(linkage, record);
case QEnum:
return new QtPDOMQEnum(linkage, record);
case QProperty:
return new QtPDOMProperty(linkage, record);
}
return null;

View file

@ -0,0 +1,245 @@
/*
* Copyright (c) 2013 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.qt.internal.core.pdom;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.cdt.internal.core.pdom.db.IString;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.cdt.qt.core.index.IQProperty;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
public class QtPDOMProperty extends QtPDOMBinding implements IField {
private static int offsetInitializer = QtPDOMBinding.Field.Last.offset;
protected static enum Field {
Type(Database.PTR_SIZE),
Attributes(Database.PTR_SIZE),
Last(0);
public final int offset;
private Field(int sizeof) {
this.offset = offsetInitializer;
offsetInitializer += sizeof;
}
public long getRecord(long baseRec) {
return baseRec + offset;
}
}
private QtPDOMQObject qobj;
public QtPDOMProperty(QtPDOMLinkage linkage, long record) {
super(linkage, record);
}
public QtPDOMProperty(QtPDOMLinkage linkage, PDOMBinding parent, QtPropertyName qtName) throws CoreException {
super(linkage, parent, qtName);
setType(qtName.getType());
if (!(parent instanceof QtPDOMQObject))
this.qobj = null;
else {
this.qobj = (QtPDOMQObject) parent;
this.qobj.addChild(this);
}
}
@Override
protected int getRecordSize() {
return Field.Last.offset;
}
@Override
public int getNodeType() {
return QtPDOMNodeType.QProperty.Type;
}
public void delete() throws CoreException {
long fieldRec = getDB().getRecPtr(Field.Type.getRecord(record));
if (fieldRec != 0)
getDB().getString(fieldRec).delete();
getDB().putRecPtr(Field.Type.getRecord(record), 0);
}
public void setType(String type) throws CoreException {
long rec = getDB().getRecPtr(Field.Type.getRecord(record));
if (rec != 0) {
IString typeStr = getDB().getString(rec);
if (type == null) {
typeStr.delete();
return;
}
// There is nothing to do if the database already stores the same name.
if (type.equals(typeStr.getString()))
return;
}
getDB().putRecPtr(Field.Type.getRecord(record), getDB().newString(type).getRecord());
}
// TODO IType?
public String getTypeStr() throws CoreException {
long rec = getDB().getRecPtr(Field.Type.getRecord(record));
if (rec == 0)
return null;
return getDB().getString(rec).getString();
}
public void setAttributes(Attribute[] attributes) throws CoreException {
long rec = getDB().getRecPtr(Field.Attributes.getRecord(record));
QtPDOMArray<Attribute> pdomArray = new QtPDOMArray<Attribute>(getQtLinkage(), Attribute.Codec, rec);
rec = pdomArray.set(attributes);
getDB().putRecPtr(Field.Attributes.getRecord(record), rec);
}
public Attribute[] getAttributes() throws CoreException {
long rec = getDB().getRecPtr(Field.Attributes.getRecord(record));
QtPDOMArray<Attribute> pdomArray = new QtPDOMArray<Attribute>(getQtLinkage(), Attribute.Codec, rec);
return pdomArray.get();
}
@Override
public ICompositeType getCompositeTypeOwner() {
if (qobj == null)
try {
IBinding parent = getParentBinding();
if (parent instanceof QtPDOMQObject)
qobj = (QtPDOMQObject) parent;
} catch(CoreException e) {
QtPlugin.log(e);
}
return qobj;
}
/**
* TODO use the real type?
*/
private static final IType Type = new IType() {
@Override
public Object clone() {
// This is a stateless singleton instance, there is nothing to clone.
return this;
}
@Override
public boolean isSameType(IType type) {
return type == this;
}
};
@Override
public IType getType() {
return Type;
}
@Override
public IValue getInitialValue() {
return null;
}
@Override
public boolean isStatic() {
return false;
}
@Override
public boolean isExtern() {
return false;
}
@Override
public boolean isAuto() {
return false;
}
@Override
public boolean isRegister() {
return false;
}
public static class Attribute {
public final IQProperty.Attribute attr;
public final String value;
public final long cppRecord;
public Attribute(IQProperty.Attribute attr, String value) {
this.attr = attr;
this.value = value;
this.cppRecord = 0;
}
public Attribute(IQProperty.Attribute attr, String value, PDOMBinding cppBinding) {
this.attr = attr;
this.value = value;
this.cppRecord = cppBinding == null ? 0 : cppBinding.getRecord();
}
private Attribute(IQProperty.Attribute attr, String value, long cppRecord) {
this.attr = attr;
this.value = value;
this.cppRecord = cppRecord;
}
private static final IQtPDOMCodec<Attribute> Codec = new IQtPDOMCodec<Attribute>() {
@Override
public int getElementSize() {
return 1 + Database.PTR_SIZE + Database.PTR_SIZE;
}
@Override
public Attribute[] allocArray(int count) {
return new Attribute[count];
}
@Override
public Attribute decode(QtPDOMLinkage linkage, long record) throws CoreException {
byte attrId = linkage.getDB().getByte(record);
long valRec = linkage.getDB().getRecPtr(record + 1);
long cppRec = linkage.getDB().getRecPtr(record + 1 + Database.PTR_SIZE);
if (attrId < 0 || attrId >= IQProperty.Attribute.values().length)
throw QtPlugin.coreException("invalid QProperty attribute id read from datbase, was " + attrId);
IQProperty.Attribute attr = IQProperty.Attribute.values()[attrId];
String val = valRec == 0 ? "" : linkage.getDB().getString(valRec).getString();
return new Attribute(attr, val, cppRec);
}
@Override
public void encode(QtPDOMLinkage linkage, long record, Attribute element) throws CoreException {
linkage.getDB().putByte(record, (byte) element.attr.ordinal());
// Delete the existing strings then create and store new ones.
long rec = linkage.getDB().getRecPtr(record + 1);
if (rec != 0)
linkage.getDB().getString(rec).delete();
if (element == null || element.value == null)
linkage.getDB().putRecPtr(record + 1, 0);
else
linkage.getDB().putRecPtr(record + 1, linkage.getDB().newString(element.value).getRecord());
linkage.getDB().putRecPtr(record + 1 + Database.PTR_SIZE, element.cppRecord);
}
};
}
}

View file

@ -11,6 +11,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.eclipse.cdt.core.dom.ILinkage;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.core.dom.ast.ICompositeType;
@ -19,7 +20,9 @@ import org.eclipse.cdt.core.dom.ast.IEnumerator;
import org.eclipse.cdt.core.dom.ast.IField;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.IValue;
import org.eclipse.cdt.internal.core.pdom.db.Database;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMLinkage;
import org.eclipse.cdt.qt.core.QtPlugin;
import org.eclipse.core.runtime.CoreException;
@ -29,6 +32,7 @@ public class QtPDOMQEnum extends QtPDOMBinding implements IField {
private static int offsetInitializer = QtPDOMBinding.Field.Last.offset;
protected static enum Field {
Flags(1),
CppRecord(Database.PTR_SIZE),
Last(0);
public final int offset;
@ -51,13 +55,14 @@ public class QtPDOMQEnum extends QtPDOMBinding implements IField {
super(linkage, record);
}
public QtPDOMQEnum(QtPDOMLinkage linkage, PDOMBinding parent, QtBinding binding) throws CoreException {
super(linkage, parent, binding);
public QtPDOMQEnum(QtPDOMLinkage linkage, PDOMBinding parent, IASTName qtName, IASTName cppName) throws CoreException {
super(linkage, parent, qtName);
getDB().putRecPtr(Field.CppRecord.getRecord(record), linkage.getCPPRecord(cppName));
// The flags are set in several sections, and then written to the Database in one operation.
byte flags = 0;
IASTName qtName = binding.getQtName();
if (qtName instanceof QtEnumName
&& ((QtEnumName) qtName).isFlag())
flags |= IS_FLAG_MASK;
@ -78,6 +83,25 @@ public class QtPDOMQEnum extends QtPDOMBinding implements IField {
return Field.Last.offset;
}
public IEnumeration getCppEnumeration() throws CoreException {
long cppRec = getDB().getRecPtr(Field.CppRecord.getRecord(record));
if (cppRec == 0)
return null;
PDOMLinkage cppLinkage = getPDOM().getLinkage(ILinkage.CPP_LINKAGE_ID);
if (cppLinkage == null)
return null;
PDOMBinding cppBinding = cppLinkage.getBinding(cppRec);
// TODO
if (cppBinding == null)
return null;
cppBinding.getAdapter(IEnumeration.class);
return cppBinding instanceof IEnumeration ? (IEnumeration) cppBinding : null;
}
public boolean isFlag() throws CoreException {
byte flags = getDB().getByte(Field.Flags.getRecord(record));
return (flags & IS_FLAG_MASK) == IS_FLAG_MASK;
@ -103,12 +127,8 @@ public class QtPDOMQEnum extends QtPDOMBinding implements IField {
}
public List<IEnumerator> getEnumerators() throws CoreException {
IBinding cppBinding = getCppBinding();
if (!(cppBinding instanceof IEnumeration))
return Collections.emptyList();
IEnumeration cppEnum = (IEnumeration) cppBinding;
return Arrays.asList(cppEnum.getEnumerators());
IEnumeration cppEnum = getCppEnumeration();
return cppEnum == null ? Collections.<IEnumerator>emptyList() : Arrays.asList(cppEnum.getEnumerators());
}
/**

View file

@ -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<ClassInfo>(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<String, String> 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<Map.Entry<String, String>> iterator = classInfos.entrySet().iterator();
for(int i = 0; i < array.length && iterator.hasNext(); ++i) {
Map.Entry<String, String> entry = iterator.next();
array[i] = new ClassInfo(entry.getKey(), entry.getValue());
}
// 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<ClassInfo> pdomArray = new QtPDOMArray<QtPDOMQObject.ClassInfo>(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<String, String> classInfo : classInfos.entrySet()) {
IString key = getDB().newString(classInfo.getKey());
IString val = getDB().newString(classInfo.getValue());
getDB().putRecPtr(b, key.getRecord()); b += Database.PTR_SIZE;
getDB().putRecPtr(b, val.getRecord()); b += Database.PTR_SIZE;
}
// Put the new block into the PDOM.
getDB().putRecPtr(Field.ClassInfos.getRecord(record), block);
// Update the record that is stored in the receiver's field.
getDB().putRecPtr(Field.ClassInfos.getRecord(record), arrayRec);
}
public Map<String, String> getClassInfos() throws CoreException {
Map<String, String> classInfos = new LinkedHashMap<String, String>();
// ClassInfo was not supported before version 2.
if (getQtLinkage().getVersion() < 2)
// Make sure the version of the linkage contains this field.
if (!Field.ClassInfos.isSupportedIn(getQtLinkage()))
return classInfos;
long block = getDB().getRecPtr(Field.ClassInfos.getRecord(record));
if (block != 0) {
int numEntries = getDB().getInt(block);
block += Database.INT_SIZE;
// Read the array from the Database and insert the elements into the Map that is to be returned.
long arrayRec = getDB().getRecPtr(Field.ClassInfos.getRecord(record));
QtPDOMArray<ClassInfo> pdomArray = new QtPDOMArray<QtPDOMQObject.ClassInfo>(getQtLinkage(), ClassInfo.Codec, arrayRec);
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<QtPDOMQObject> findBases() throws CoreException {
IBinding cppBinding = getCppBinding();
if (!(cppBinding instanceof ICPPClassType))
ICPPClassType cppClassType = getCppClassType();
if (cppClassType == null)
return Collections.emptyList();
List<QtPDOMQObject> bases = new ArrayList<QtPDOMQObject>();
for (ICPPBase base : ((ICPPClassType) cppBinding).getBases()) {
for (ICPPBase base : cppClassType.getBases()) {
if (base.getVisibility() != ICPPBase.v_public)
continue;
@ -170,18 +199,12 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
if (baseCls == null)
continue;
IPDOMBinding pdomBase = (IPDOMBinding) baseCls.getAdapter(IPDOMBinding.class);
if (pdomBase == null)
continue;
QtPDOMBinding qtPDOMBinding = getQtLinkage().findFromCppRecord(pdomBase.getRecord());
if (qtPDOMBinding == null)
continue;
QtPDOMQObject pdomQObj = (QtPDOMQObject) qtPDOMBinding.getAdapter(QtPDOMQObject.class);
if (pdomQObj != null)
bases.add(pdomQObj);
PDOMBinding pdomBinding = (PDOMBinding) baseCls.getAdapter(PDOMBinding.class);
QtPDOMQObject baseQObj = ASTNameReference.findFromBinding(QtPDOMQObject.class, pdomBinding);
if (baseQObj != null)
bases.add(baseQObj);
}
return bases;
}
@ -213,6 +236,18 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
children.addMember(child);
}
public <T extends IField> List<T> getFields(Class<T> cls) throws CoreException {
QtPDOMVisitor.All<T> collector = new QtPDOMVisitor.All<T>(cls);
try {
children.accept(collector);
} catch(CoreException e) {
QtPlugin.log(e);
return Collections.emptyList();
}
return collector.list;
}
@Override
public IField[] getFields() {
QtPDOMVisitor.All<IField> collector = new QtPDOMVisitor.All<IField>(IField.class);
@ -241,9 +276,7 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
@Override
public IScope getCompositeScope() {
try {
IBinding cppBinding = getCppBinding();
if (cppBinding instanceof ICompositeType)
return ((ICompositeType) cppBinding).getCompositeScope();
return getCppClassType().getCompositeScope();
} catch(CoreException e) {
QtPlugin.log(e);
}
@ -255,4 +288,46 @@ public class QtPDOMQObject extends QtPDOMBinding implements ICompositeType {
public Object clone() {
throw new UnsupportedOperationException();
}
private static class ClassInfo {
public final String key;
public final String value;
public ClassInfo(String key, String value) {
this.key = key;
this.value = value;
}
public static final IQtPDOMCodec<ClassInfo> Codec = new IQtPDOMCodec<ClassInfo>() {
@Override
public int getElementSize() {
return 2 * Database.PTR_SIZE;
}
@Override
public ClassInfo[] allocArray(int count) {
return new ClassInfo[count];
}
@Override
public ClassInfo decode(QtPDOMLinkage linkage, long record) throws CoreException {
long keyRec = linkage.getDB().getRecPtr(record);
long valRec = linkage.getDB().getRecPtr(record + Database.PTR_SIZE);
return new ClassInfo(linkage.getDB().getString(keyRec).getString(), linkage.getDB().getString(valRec).getString());
}
@Override
public void encode(QtPDOMLinkage linkage, long record, ClassInfo element) throws CoreException {
// Delete the existing strings then create and store new ones.
long rec = linkage.getDB().getRecPtr(record);
if (rec != 0)
linkage.getDB().getString(rec).delete();
linkage.getDB().putRecPtr(record, element == null ? 0 : linkage.getDB().newString(element.key).getRecord());
rec = linkage.getDB().getRecPtr(record + Database.PTR_SIZE);
if (rec != 0)
linkage.getDB().getString(rec).delete();
linkage.getDB().putRecPtr(record + Database.PTR_SIZE, element == null ? 0 : linkage.getDB().newString(element.value).getRecord());
}
};
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2013 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.cdt.qt.internal.core.pdom;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IBinding;
import org.eclipse.cdt.internal.core.pdom.dom.PDOMBinding;
import org.eclipse.cdt.qt.core.index.IQProperty;
import org.eclipse.core.runtime.CoreException;
@SuppressWarnings("restriction")
public class QtPropertyName extends AbstractQObjectFieldName implements IQtASTName {
private String type;
private List<QtPDOMProperty.Attribute> attributes = new ArrayList<QtPDOMProperty.Attribute>();
public QtPropertyName(QObjectName qobjName, IASTName ast, String name, QtASTImageLocation location) {
super(qobjName, ast, name, location);
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
/**
* This permits storage of duplicate attributes, a Codan checker should flag this as an error, but
* while the invalid code exists, the references should continue to be properly resolved.
*/
public void addAttribute(IQProperty.Attribute attr, String value) {
attributes.add(new QtPDOMProperty.Attribute(attr, value));
}
/**
* This permits storage of duplicate attributes, a Codan checker should flag this as an error, but
* while the invalid code exists, the references should continue to be properly resolved.
*/
public void addAttribute(IQProperty.Attribute attr, String value, IBinding cppBinding) {
PDOMBinding pdomBinding = cppBinding == null ? null : (PDOMBinding) cppBinding.getAdapter(PDOMBinding.class);
attributes.add(new QtPDOMProperty.Attribute(attr, value, pdomBinding));
}
@Override
public QtPDOMBinding createPDOMBinding(QtPDOMLinkage linkage) throws CoreException {
QtPDOMProperty pdom = new QtPDOMProperty(linkage, getOwner(linkage), this);
pdom.setAttributes(attributes.toArray(new QtPDOMProperty.Attribute[attributes.size()]));
return pdom;
}
}

View file

@ -7,11 +7,18 @@
*/
package org.eclipse.cdt.qt.tests;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.cdt.qt.core.index.IQEnum;
import org.eclipse.cdt.qt.core.index.IQObject;
import org.eclipse.cdt.qt.core.index.IQProperty;
import org.eclipse.cdt.qt.core.index.IQProperty.Attribute;
import org.eclipse.cdt.qt.core.index.QtIndex;
public class QObjectTests extends BaseQtTestCase {
@ -48,41 +55,6 @@ public class QObjectTests extends BaseQtTestCase {
assertNull(qobj_D2);
}
// #include "junit-QObject.hh"
// class B : public QObject
// {
// Q_OBJECT
// Q_CLASSINFO( "key1", "value1" )
// Q_CLASSINFO( "key2", "value\"2" )
// public:
// bool isAllowed() const { return false; }
// };
// class D : public B
// {
// Q_OBJECT
// Q_CLASSINFO( "key2", "overridden value" )
// public:
// bool isAllowed() const { return false; }
// };
public void testClassInfos() throws Exception {
loadComment("classinfos.hh");
QtIndex qtIndex = QtIndex.getIndex(fProject);
assertNotNull(qtIndex);
IQObject qobj_b = qtIndex.findQObject(new String[]{ "B" });
if (!isIndexOk("B", qobj_b))
return;
assertNotNull(qobj_b);
assertEquals("value1", qobj_b.getClassInfo("key1"));
assertEquals("value\\\"2", qobj_b.getClassInfo("key2"));
IQObject qobj_d = qtIndex.findQObject(new String[]{ "D" });
assertNotNull(qobj_d);
assertEquals("value1", qobj_d.getClassInfo("key1")); // inherited
assertEquals("overridden value", qobj_d.getClassInfo("key2"));
}
// #include "junit-QObject.hh"
// template <typename T> class QList {};
// class QString {};
@ -210,4 +182,237 @@ public class QObjectTests extends BaseQtTestCase {
fail("unexpected Q_FLAGS " + qEnum.getName());
}
}
// #include "junit-QObject.hh"
// class B : public QObject
// {
// Q_OBJECT
// Q_PROPERTY(bool allowed READ isAllowed)
// public:
// bool isAllowed() const { return false; }
// };
public void testOwner() throws Exception {
loadComment("owner.hh");
QtIndex qtIndex = QtIndex.getIndex(fProject);
assertNotNull(qtIndex);
IQObject qobj = qtIndex.findQObject(new String[]{ "B" });
if (!isIndexOk("B", qobj))
return;
assertNotNull(qobj);
Collection<IQProperty> properties = qobj.getProperties().locals();
assertNotNull(properties);
assertEquals(1, properties.size());
IQProperty property = properties.iterator().next();
assertNotNull(property);
assertTrue(qobj == property.getOwner());
}
// #include "junit-QObject.hh"
// template <typename T> class T {};
// class Q : public QObject
// {
// Q_OBJECT
//
// bool getProp() const;
//
// // strange cases found by grep'ing for Q_PROPERTY in the qt4 headers
// Q_PROPERTY( bool prop1 READ getProp )
// Q_PROPERTY( T<bool> prop2 READ getProp )
// Q_PROPERTY( T<bool *> prop3 READ getProp )
// Q_PROPERTY( bool *prop4 READ getProp )
// Q_PROPERTY( bool prop5 )
// Q_PROPERTY( bool *prop6 )
//
// Q_PROPERTY( bool read1 READ readMethod )
// Q_PROPERTY( bool read2 READ readMethod2 FINAL )
//
// // from qtoolbar.h
// Q_PROPERTY( Namespace::Type allowedAreas1 )
// Q_PROPERTY( Qt::ToolBarAreas allowedAreas2 READ allowedAreas WRITE setAllowedAreas
// DESIGNABLE (qobject_cast<QMainWindow *>(parentWidget()) != 0)
// NOTIFY allowedAreasChanged )
//
// bool readMethod();
// bool readMethod2() const { return false; }
// Qt::ToolBarAreas allowedAreas() const;
// void setAllowedAreas( Qt::ToolBarAreas ) { }
// Q_SIGNAL void allowedAreasChanged();
// };
public void testQProperties() throws Exception {
loadComment("q_property.hh");
QtIndex qtIndex = QtIndex.getIndex(fProject);
assertNotNull(qtIndex);
IQObject qobj = qtIndex.findQObject(new String[]{ "Q" });
if (!isIndexOk("Q", qobj))
return;
assertNotNull(qobj);
assert_checkQProperties(
qobj,
new ExpectedQProperty("bool", "prop1", Attribute.READ, "getProp"),
new ExpectedQProperty("T<bool>", "prop2", Attribute.READ, "getProp"),
new ExpectedQProperty("T<bool *>", "prop3", Attribute.READ, "getProp"),
new ExpectedQProperty("bool *", "prop4", Attribute.READ, "getProp"),
new ExpectedQProperty("bool", "prop5"),
new ExpectedQProperty("bool *", "prop6"),
new ExpectedQProperty("bool", "read1", Attribute.READ, "readMethod"),
new ExpectedQProperty("bool", "read2", Attribute.READ ,"readMethod2", Attribute.FINAL),
new ExpectedQProperty("Namespace::Type", "allowedAreas1" ),
new ExpectedQProperty("Qt::ToolBarAreas", "allowedAreas2",
Attribute.READ, "allowedAreas",
Attribute.WRITE, "setAllowedAreas",
Attribute.DESIGNABLE, "(qobject_cast<QMainWindow *>(parentWidget()) != 0)",
Attribute.NOTIFY, "allowedAreasChanged")
);
}
// #include "junit-QObject.hh"
// class B : public QObject
// {
// Q_OBJECT
// Q_PROPERTY(bool allowed READ isAllowed)
// public:
// bool isAllowed() const { return false; }
// };
// class D1 : public B
// {
// Q_OBJECT
// Q_PROPERTY(bool allowed READ isAllowed_d)
// public:
// bool isAllowed_d() const { return false; }
// };
public void testGetOverridden() throws Exception {
loadComment("getOverridden.hh");
QtIndex qtIndex = QtIndex.getIndex(fProject);
assertNotNull(qtIndex);
IQObject base_qobj = qtIndex.findQObject(new String[]{ "B" });
if (!isIndexOk("B", base_qobj))
return;
assertNotNull(base_qobj);
IQObject.IMembers<IQProperty> base_qprops = base_qobj.getProperties();
assertNotNull(base_qprops);
assertEquals(1, base_qprops.all().size());
assertEquals(1, base_qprops.locals().size());
assertEquals(1, base_qprops.withoutOverrides().size());
IQObject derived_qobj1 = qtIndex.findQObject(new String[]{ "D1" });
assertNotNull(derived_qobj1);
IQObject.IMembers<IQProperty> derived_qprops1 = derived_qobj1.getProperties();
assertNotNull(derived_qprops1);
assertEquals(2, derived_qprops1.all().size());
assertEquals(1, derived_qprops1.locals().size());
assertEquals(1, derived_qprops1.withoutOverrides().size());
}
// #include "junit-QObject.hh"
// class B : public QObject
// {
// Q_OBJECT
// Q_CLASSINFO( "key1", "value1" )
// Q_CLASSINFO( "key2", "value\"2" )
// public:
// bool isAllowed() const { return false; }
// };
// class D : public B
// {
// Q_OBJECT
// Q_CLASSINFO( "key2", "overridden value" )
// public:
// bool isAllowed() const { return false; }
// };
public void testClassInfos() throws Exception {
loadComment("classinfos.hh");
QtIndex qtIndex = QtIndex.getIndex(fProject);
assertNotNull(qtIndex);
IQObject qobj_b = qtIndex.findQObject(new String[]{ "B" });
if (!isIndexOk("B", qobj_b))
return;
assertNotNull(qobj_b);
assertEquals("value1", qobj_b.getClassInfo("key1"));
assertEquals("value\\\"2", qobj_b.getClassInfo("key2"));
IQObject qobj_d = qtIndex.findQObject(new String[]{ "D" });
assertNotNull(qobj_d);
assertEquals("value1", qobj_d.getClassInfo("key1")); // inherited
assertEquals("overridden value", qobj_d.getClassInfo("key2"));
}
private static class ExpectedQProperty {
public final String type;
public final String name;
Object[] attributes;
public ExpectedQProperty(String type, String name, Object... attributes) {
this.type = type;
this.name = name;
this.attributes = attributes;
}
}
/**
* A utility method for testing Q_PROPERTYs. The given object is checked for the list of
* values. Only the locally declared properties are checked and the list must be complete.
*/
private static void assert_checkQProperties(IQObject qobj, ExpectedQProperty... expectedProperties) throws Exception {
// this map is used to make sure that all expected attributes are found
Map<String, ExpectedQProperty> qprops = new HashMap<String, QObjectTests.ExpectedQProperty>();
for(ExpectedQProperty qprop : expectedProperties)
if (qprops.containsKey(qprop.name))
fail("duplicate properties in expected list " + qprop.name);
else
qprops.put(qprop.name, qprop);
for(IQProperty qprop : qobj.getProperties().locals()) {
ExpectedQProperty expected = qprops.remove(qprop.getName());
assertNotNull("unexpected or duplicate attribute " + qprop.getName(), expected);
assertEquals("unexpected type for " + expected.name, expected.type, qprop.getType());
assertEquals("unexpected type for " + expected.name, expected.name, qprop.getName());
// make sure that all attributes that were found were expected
Set<Attribute> allAttrs = new HashSet<Attribute>(Arrays.asList(Attribute.values()));
for(int i = 0; i < expected.attributes.length; ++i) {
Attribute attr = (Attribute)expected.attributes[i];
// make sure the test is valid -- search for each attribute at most once
assertTrue(allAttrs.remove(attr));
if (!attr.hasValue)
assertNotNull("missing " + attr.toString(), attr.valueIn(qprop));
else if(i >= (expected.attributes.length - 1)
|| expected.attributes[i + 1] instanceof Attribute)
fail("INVALID TEST CASE: " + attr + " should have a value, but one was not provided" );
else {
Object exp = expected.attributes[++i];
assertEquals(attr.toString(), exp, attr.valueIn(qprop));
}
}
// make sure there is no value for all other attributes
for(Attribute attr : allAttrs)
assertTrue("unexpectedly found value for " + attr, attr.valueIn(qprop) == null);
}
// make sure that all expected properties were found
StringBuilder missingAttrs = new StringBuilder();
for(String propName : qprops.keySet()) {
if (missingAttrs.length() > 0)
missingAttrs.append(", ");
missingAttrs.append(propName);
}
assertTrue("missing properties " + missingAttrs.toString(), missingAttrs.length() == 0);
}
}