When I try to subclass a GTK IconView in Vala using Glade, I get a segmentation fault. Is this a bug in Vala, or am I just doing something wrong? This is using vala 0.42.3. Maybe this is related to how IconView doesn't have a base() constructor? (see: Chain up to 'Gtk.Box.new' not supported)
test.vala:
using Gtk;
public class IconViewSubclass : Gtk.IconView {
public IconViewSubclass() {
}
}
public static int main(string[] args) {
Gtk.init(ref args);
var builder = new Builder.from_file("test.glade");
var window = builder.get_object("window") as Window;
var iconViewSubclass = builder.get_object("iconViewSubclass") as IconViewSubclass;
iconViewSubclass.set_pixbuf_column(0);
iconViewSubclass.set_text_column(1);
window.show_all();
Gtk.main();
return 0;
}
test.glade:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkListStore" id="store">
<columns>
<!-- column-name pixbuf -->
<column type="GdkPixbuf"/>
<!-- column-name text -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkWindow" id="window">
<property name="can_focus">False</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkIconView" id="iconViewSubclass">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin">6</property>
<property name="model">store</property>
</object>
</child>
</object>
</interface>
$ valac --pkg gtk+-3.0 test.vala && ./test
Segmentation fault
It looks like you need to let the Gtk.Builder know IconViewSubclass exists using expose_object(). This allows the sub-type to be used in the Builder UI definition file. Here's an example that compiles and does not segfault:
test.ui:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.18"/>
<object class="GtkWindow" id="window">
<property name="window-position">GTK_WIN_POS_CENTER</property>
<property name="default-height">250</property>
<property name="default-width">250</property>
<child>
<placeholder/>
</child>
<child>
<object class="IconViewSubclass" id="iconViewSubclass">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="margin">6</property>
</object>
</child>
</object>
</interface>
and test.vala:
using Gtk;
public class IconViewSubclass : Gtk.IconView {}
public static int main(string[] args) {
Gtk.init(ref args);
var builder = new Builder ();
builder.expose_object ("IconViewSubclass", new IconViewSubclass ());
try {
builder.add_from_file ("test.ui");
} catch (Error error) {
print (#"$(error.message)");
}
var window = builder.get_object ("window") as Window;
var iconViewSubclass = (IconViewSubclass)builder.get_object ("iconViewSubclass");
iconViewSubclass.set_pixbuf_column (0);
iconViewSubclass.set_text_column (1);
window.show_all();
Gtk.main();
return 0;
}
You may want to look into using templates with Vala [GtkTemplate], [GtkChild] and [GtkCallback] attributes. The attributes will tell Vala to generate the boiler plate code for you.
AIThomas' code worked great, however, in order to continue editing the UI file in Glade I had to add a catalog file, as follows:
<?xml version="1.0" encoding="UTF-8"?>
<glade-catalog name="test" depends="gtk+">
<glade-widget-classes>
<glade-widget-class name="IconViewSubclass" generic-name="iconviewsubclass" title="IconViewSubclass" parent="GtkIconView"/>
</glade-widget-classes>
<glade-widget-group name="test" title="test">
<glade-widget-class-ref name="IconViewSubclass"/>
</glade-widget-group>
</glade-catalog>
I also discovered that the subclass name in Vala must correspond exactly with the widget name in the UI XML, i.e. changing IconViewSubclass to IconViewSubclass2 in test.vala will give you:
$ valac --pkg gtk+-3.0 test.vala && ./test
test.ui:14:1 Invalid object type 'IconViewSubclass'Segmentation fault
You can also use a namespace as part of the class name, i.e. namespace Foo { public class Bar : Baz } should work for a widget of type FooBar in the UI XML file.
Related
Asked in Liferay Forums
I've created a permission helper class for my Question entity following the steps explained here:
package net.carlosduran.nomedes.web.internal.security.permission.resource;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.security.permission.PermissionChecker;
import com.liferay.portal.kernel.security.permission.resource.ModelResourcePermission;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import net.carlosduran.nomedes.db.model.Question;
#Component(immediate = true)
public class QuestionPermission {
public static boolean contains(
PermissionChecker permissionChecker, Question question, String actionId) throws PortalException {
return _questionModelResourcePermission.contains(permissionChecker, question, actionId);
}
public static boolean contains(
PermissionChecker permissionChecker, long entryId, String actionId) throws PortalException {
return _questionModelResourcePermission.contains(permissionChecker, entryId, actionId);
}
#Reference(
target = "(model.class.name=net.carlosduran.nomedes.db.model.Question)",
unbind = "-")
protected void setEntryModelPermission(ModelResourcePermission<Question> modelResourcePermission) {
_questionModelResourcePermission = modelResourcePermission;
}
private static ModelResourcePermission<Question> _questionModelResourcePermission;
}
In a MVCRenderCommand class I reference it this way:
#Reference
protected QuestionPermission _questionPermission;
If I include this reference, the render class doesn't work (I've tried it with different render classes).
In the moment I delete it, the render class works without a problem.
The code for the service.xml file is this:
<?xml version="1.0"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 7.4.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_7_4_0.dtd">
<service-builder dependency-injector="ds" package-path="net.carlosduran.nomedes.db">
<namespace>Nomedes</namespace>
<entity name="Question" local-service="true" uuid="true">
<!-- PK fields -->
<column name="questionId" primary="true" type="long"></column>
<!-- Group instance -->
<column name="groupId" type="long"></column>
<!-- Audit fields -->
<column name="companyId" type="long"></column>
<column name="userId" type="long"></column>
<column name="userName" type="String"></column>
<column name="createDate" type="Date"></column>
<column name="modifiedDate" type="Date"></column>
<column name="title" type="String"></column>
<column name="summary" type="String"></column>
<column name="description" type="String"></column>
<column name="status" type="int" />
<column name="statusByUserId" type="long" />
<column name="statusByUserName" type="String" />
<column name="statusDate" type="Date" />
<finder name="GroupId" return-type="Collection">
<finder-column name="groupId"></finder-column>
</finder>
<reference entity="Group" package-path="com.liferay.portal"></reference>
</entity>
<exceptions>
<exception>QuestionValidation</exception>
</exceptions>
</service-builder>
Can anyone tell me what's wrong? Thanks
The problem was that I miss to add a service attribute to the component annotation in the QuestionPermission class.
service = QuestionPermission.class
After adding it, it works fine. It would look like this:
#Component(
immediate = true,
service = QuestionPermission.class
)
I have the following class:
public class Foo
{
//...
protected int? someData;
public int? SomeData
{
get {return someData;}
set {someData = value;}
}
//...
}
This class is mapped in HBM file:
<class name="Foo" table="Foo">
//....
<property name="someData" access="field" column="SOME_DATA" />
//....
</class>
For legacy reasons, the columns were mapped to a fields (lowercase), which are unaccessible (not public properties - uppercase).
These lowercase mappings are using multiple times in the project as part of HQL strings, criteria-based queries and so on. It is close to impossible to find all usages.
The problem is that such usage makes it impossible to use even on simple lambda:
session.Query<Foo>().Select(f=>f.SomeData)
throws error, as uppercase "SomeData" is not mapped, and
session.Query<Foo>().Select(f=>f.someData)
does not compile, as lowercase "someData" is protected.
What happens, if I will map BOTH field and property:
<property name="someData" access="field" column="SOME_DATA" />
<property name="SomeData" access="property" column="SOME_DATA" />
I tried quickly, and it seems to work, but will it have any drawback?
Is there any other simple solution, that will not require editing every criteria-based query in project?
Thanks for any help.
You can map the same column multiple times but with recent NHibernate versions only one of the properties need to be mapped as modifiable. So just add insert="false" update="false" to your additional mappings to avoid any issues in future:
<property name="someData" access="field" column="SOME_DATA" />
<property name="SomeData" access="property" column="SOME_DATA" update="false" insert="false" />
I have read that this mapping is not possible in NHibernate 3.3:
<class name="Digital" table="DIGITALS">
<composite-id>
<key-many-to-one name="Person" class="Person" column="PERSONID" />
<key-property name="Id" column="ID">
**<generator class="increment"/>**
<key-property/>
</composite-id>
<property name="Nombre" column="NOMBRE" />
Basically I need a composite-id's property to be calculated automatically by NH.
Maybe exists a technique to get something similar?
Thanks in advance.
you have to implement it yourself since CompositeIds are always generatedby assigned for NH
class Digital
{
private static long number = 0;
private static long NextNumber()
{
return Interlocked.Increment(ref number);
}
public Digital()
{
Id = NextNumber();
}
}
I am trying to create a table-per-hierarchy mapping using NHibernate 2.0.1.
I have a base class with properties that exist for each subclass that other classes inherit from. All of these objects are persisted to one table called Messages that contain all of the possible fields for each class. There is a SourceID which is the discriminator and should indicate which Poco to return for each subclass. Here is my current mapping.
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="NS.Core"
namespace="NS.Core.Model">
<class name="BaseMessage" table="Messages">
<id name="MessageID" type="Int64">
<column name="MessageID" />
<generator class="native" />
</id>
<discriminator column="SourceID" type="Int32"/>
<property name="DateCreated" access="property" column="DateCreated" type="DateTime" not-null="true"/>
<property name="DatePublished" access="property" column="DatePublished" type="DateTime"/>
<property name="SourceID" access="property" column="SourceID" type="Int32"/>
<many-to-one name="User" column="UserID" access="property" cascade="none" lazy="false" fetch="join" outer-join="true" />
<subclass name="NMessage" discriminator-value="0">
<property name="Body" access="property" column="Body" type="String"/>
</subclass>
<subclass name="BMessage" discriminator-value="1">
<property name="Title" access="property" column="Title" type="String"/>
<property name="Body" access="property" column="Body" type="String"/>
</subclass>
<subclass name="CMessage" discriminator-value="2">
<property name="Url" access="property" column="Url" type="String"/>
<property name="Body" access="property" column="Body" type="String"/>
</subclass>
</class>
</hibernate-mapping>
I get an error querying saying Could not format discriminator value to SQL string of entity NS.Core.Model.BaseMessage so I put a discriminator value on this class too althout it should never return the base class. That led me to some antlr errors.
Am I taking the wrong approach to this problem? I would like to query the table and get back a list of different POCOs that all inherit from the base class. It would never return the base class itself.
below is the BaseMessage.cs
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;
namespace NS.Core.Model
{
[Serializable]
[DataContract]
[KnownType(typeof(User))]
public class BaseMessage
{
#region Private variables
private long _messageID;
private DateTime? _dateCreated;
private DateTime? _datePublished;
private int _sourceID;
private User _user = new User();
#endregion
#region Properties
[DataMember]
public virtual long MessageID
{
get { return _messageID; }
set { this._messageID = value; }
}
[DataMember]
public virtual DateTime? DateCreated
{
get { return _dateCreated; }
set { this._dateCreated = value; }
}
[DataMember]
public virtual DateTime? DatePublished
{
get { return _datePublished; }
set { this._datePublished = value; }
}
[DataMember]
public virtual int SourceID
{
get { return _sourceID; }
set { this._sourceID = value; }
}
[DataMember]
public virtual User User
{
get
{
if (this._user != null)
{ return this._user; }
else { return new User(); }
}
set { this._user = value; }
}
#endregion
}
}
The approach is sound. I have done exactly this several times.
You could make it explicit in your code my having BaseMessage's default constructor be protected.
You need to declare a discriminator-value on the base class as well.
I favor string values for discriminators, as this clearer when performing SQL queries or reports. Also, since instances of BaseMessage shoudl not exist, in would use null for its discriminator value.
<class name="BaseMessage" table="Messages" discriminator-value="null">
<id />
<discriminator column="SourceID" />
<subclass name="NMessage" discriminator-value="NMessage">
</subclass>
<subclass name="BMessage" discriminator-value="BMessage">
</subclass>
<subclass name="CMessage" discriminator-value="CMessage">
</subclass>
</class>
Also, I see that you have mapped a property to the discriminator column. You should instead have a method that returns something unique to the class - in this case the code.
Note that you cannot change the class of a mapped entity after it has been saved. Not even by changing the discriminator. If you did change it via SQL, your 2nd level cache will still hold a version with the original class.
class BaseMessage
{
public virtual string MessageType { return null; }
}
class NMessage : BaseMessage
{
public override string MessageType { return "NMessage"; }
}
Finally, your mapping file is overly verbose as it includes values which are the default. The following attributes and elements can be removed:
access="property" - this is the default
type="String" - all the types you use can be inferred from your .NET class
column="COL" - default is the same as the name
similarly for the id column element
All of your Message subclasses have property Body, so move it to the base class mapping. If this field can be longer than your database varchar, it should be a text column and have type="StringCLob" which maps to string in .NET
<class name="BaseMessage" table="Messages" discriminator-value="null">
<id name="MessageID">
<generator class="native" />
</id>
<discriminator column="SourceID"/>
<property name="DateCreated" />
<property name="DatePublished" />
<many-to-one name="User" column="UserID" cascade="none" lazy="false" fetch="join" outer-join="true" />
<property name="Body" type="StringCLob" />
<subclass name="NMessage" discriminator-value="NMessage">
</subclass>
<subclass name="BMessage" discriminator-value="BMessage">
<property name="Title" />
</subclass>
<subclass name="CMessage" discriminator-value="CMessage">
<property name="Url" />
</subclass>
</class>
I have the following mapping:
<?xml version='1.0' encoding='utf-8'?>
<hibernate-mapping
assembly='Core'
namespace='Core.Models'
xmlns='urn:nhibernate-mapping-2.2'>
<class name='Basket'>
<id name='Id'
column='id'>
<generator class='native'/>
</id>
<property name="ExternalId" />
<map name="Items" table="BasketItems" cascade="save-update">
<key column="BasketId" />
<index-many-to-many class="Product" column="ProductId" />
<element column="Quantity" type="System.Int32" />
</map>
</class>
</hibernate-mapping>
This is what the Items collection looks like:
public virtual IDictionary<Product, int> Items { get; private set; }
And I have an Add method like so:
public virtual void Add(Product product, int quantity)
{
if (Items.ContainsKey(product))
Items[product] += quantity;
else
Items.Add(product, quantity);
}
Then the client code looks a bit like this:
var basket = new Basket();
basket.Add(session.Load<Product>(productId));
session.SaveOrUpdate(basket);
Now, the issue is that this client code does save the Basket to the basket table, but does not save any items to the BasketItems table (I'm using SQL Server 2005). However, this test against an in-memory db passes:
[Test]
public void Can_save_basket_with_products() // Passes!!!
{
var b = new Basket();
b.Add(_savedProduct);
_session.SaveOrUpdate(b);
_session.Flush();
_session.Evict(b);
var fromDb = _session.Load<Basket>(b.Id);
Assert.AreNotSame(b, fromDb);
Assert.IsTrue(fromDb.Items.ContainsKey(_savedProduct));
}
Any ideas on why it won't save when I'm against my actual DB? What am I missing?
Note: I translated my entities for this example, I hope it's still understandable even if I left something in portuguese ;-)
Well, found out that I was missing the Flush in my client code. I'm using Castle Monorail and the NHibernate Facility, so it looks as though NH Facility does not flush upon session closing. At least not in a web scenario. I'm assuming the session IS being closed after each request.