From bf532924ec30123e7f9eedd0d05c00c97bb49555 Mon Sep 17 00:00:00 2001 From: Jan-Willem Harmannij Date: Sun, 15 Dec 2024 22:44:08 +0100 Subject: [PATCH 1/4] Remove redundant cast --- .../java/io/github/jwharm/javagi/test/gio/ListModelTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/gio/src/test/java/io/github/jwharm/javagi/test/gio/ListModelTest.java b/modules/gio/src/test/java/io/github/jwharm/javagi/test/gio/ListModelTest.java index 0608afc6..3814d074 100644 --- a/modules/gio/src/test/java/io/github/jwharm/javagi/test/gio/ListModelTest.java +++ b/modules/gio/src/test/java/io/github/jwharm/javagi/test/gio/ListModelTest.java @@ -27,7 +27,7 @@ public void createListModel() { assertEquals(listIndexModel.getItemType(), ListIndexModel.ListIndex.getType()); assertEquals(1000, listIndexModel.getNItems()); - var item500 = (ListIndexModel.ListIndex) listIndexModel.getItem(500); + var item500 = listIndexModel.getItem(500); assertNotNull(item500); assertEquals(500, item500.getIndex()); From 2caa5046c8dc3d081212be6af221a59a48ab4dee Mon Sep 17 00:00:00 2001 From: Jan-Willem Harmannij Date: Sun, 15 Dec 2024 22:45:40 +0100 Subject: [PATCH 2/4] Automatically register GObject properties for Java matching get/set method pairs --- .../javagi/gobject/annotations/Property.java | 3 + .../javagi/gobject/types/Properties.java | 378 ++++++++++-------- .../jwharm/javagi/gobject/types/Types.java | 2 +- .../test/gobject/PropertyBindingTest.java | 7 +- .../javagi/test/gobject/PropertyTest.java | 78 ++++ .../javagi/gtk/types/TemplateTypes.java | 2 +- 6 files changed, 298 insertions(+), 172 deletions(-) create mode 100644 modules/gobject/src/test/java/io/github/jwharm/javagi/test/gobject/PropertyTest.java diff --git a/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/annotations/Property.java b/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/annotations/Property.java index fcbf8754..5bff4729 100644 --- a/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/annotations/Property.java +++ b/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/annotations/Property.java @@ -31,10 +31,13 @@ public @interface Property { String name() default ""; Class type() default ParamSpec.class; + boolean skip() default false; boolean readable() default true; boolean writable() default true; boolean construct() default false; boolean constructOnly() default false; boolean explicitNotify() default false; boolean deprecated() default false; + + } diff --git a/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/types/Properties.java b/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/types/Properties.java index 28716e5a..6b86c9d6 100644 --- a/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/types/Properties.java +++ b/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/types/Properties.java @@ -36,13 +36,11 @@ import java.lang.invoke.MethodHandles; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.function.Consumer; import static io.github.jwharm.javagi.Constants.LOG_DOMAIN; +import static java.lang.Character.isUpperCase; /** * Helper class to register properties in a new GType. @@ -195,83 +193,103 @@ T newGObjectWithProperties(Type objectType, * Convert "CamelCase" to "kebab-case" */ private static String getPropertyName(String methodName) { - if (methodName.startsWith("get") || methodName.startsWith("set")) { - String value = methodName.substring(3); - return value.replaceAll("([a-z0-9])([A-Z])", "$1-$2") - .toLowerCase().replaceAll("\\.", ""); - } - throw new IllegalArgumentException("Cannot infer property name from method named %s" - .formatted(methodName)); + String value; + if (methodName.startsWith("is")) + value = methodName.substring(2); + else if (methodName.startsWith("get") || methodName.startsWith("set")) + value = methodName.substring(3); + else + throw new IllegalArgumentException( + "Cannot infer property name from method named " + methodName); + return value.replaceAll("([a-z0-9])([A-Z])", "$1-$2") + .toLowerCase().replaceAll("\\.", ""); + } + + private static boolean isGetter(Method method) { + if (skip(method)) + return false; + + if (method.getReturnType().equals(void.class) + || method.getParameterCount() != 0) + return false; + + String name = method.getName(); + + if (name.startsWith("get") + && name.length() > 3 + && isUpperCase(name.charAt(3))) + return true; + + // Boolean getter can be either getFoo() or isFoo() + return method.getReturnType().equals(boolean.class) + && name.startsWith("is") + && name.length() > 2 + && isUpperCase(name.charAt(2)); + } + + private static boolean isSetter(Method method) { + if (skip(method)) + return false; + + return method.getReturnType().equals(void.class) + && method.getParameterCount() == 1 + && method.getName().startsWith("set") + && method.getName().length() > 3 + && isUpperCase(method.getName().charAt(3)); + } + + private static Class getJavaType(Method method) { + if (method.getReturnType().equals(void.class)) + return method.getParameterTypes()[0]; + else + return method.getReturnType(); } /* * Infer the ParamSpec class from the Java class that is used in the * getter/setter method. */ - private static Class inferType(Method method) { - // Determine the Java class of the property - Class cls; - if ((! method.getReturnType().equals(void.class)) - && method.getParameterCount() == 0) { - // Getter - cls = method.getReturnType(); - } else if (method.getReturnType().equals(void.class) - && method.getParameterCount() == 1) { - // Setter - cls = method.getParameterTypes()[0]; - } else { - GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, - "Invalid property getter/setter %s in class %s\n", - method.getName(), method.getDeclaringClass().getName()); - return null; - } - - // Infer the ParamSpec from the Java class. - if (cls.equals(boolean.class) || cls.equals(Boolean.class)) + private static Class inferType(Class type) { + if (type.equals(boolean.class) || type.equals(Boolean.class)) return ParamSpecBoolean.class; - else if (cls.equals(byte.class) || cls.equals(Byte.class)) + else if (type.equals(byte.class) || type.equals(Byte.class)) return ParamSpecChar.class; - else if (cls.equals(char.class) || cls.equals(Character.class)) + else if (type.equals(char.class) || type.equals(Character.class)) return ParamSpecChar.class; - else if (cls.equals(double.class) || cls.equals(Double.class)) + else if (type.equals(double.class) || type.equals(Double.class)) return ParamSpecDouble.class; - else if (cls.equals(float.class) || cls.equals(Float.class)) + else if (type.equals(float.class) || type.equals(Float.class)) return ParamSpecFloat.class; - else if (cls.equals(int.class) || cls.equals(Integer.class)) + else if (type.equals(int.class) || type.equals(Integer.class)) return ParamSpecInt.class; - else if (cls.equals(long.class) || cls.equals(Long.class)) + else if (type.equals(long.class) || type.equals(Long.class)) return ParamSpecLong.class; - else if (cls.equals(String.class)) + else if (type.equals(String.class)) return ParamSpecString.class; - else if (Type.class.isAssignableFrom(cls)) + else if (Type.class.isAssignableFrom(type)) return ParamSpecGType.class; // GObject class - else if (GObject.class.isAssignableFrom(cls)) + else if (GObject.class.isAssignableFrom(type)) return ParamSpecObject.class; // Struct - else if (ProxyInstance.class.isAssignableFrom(cls)) + else if (ProxyInstance.class.isAssignableFrom(type)) return ParamSpecBoxed.class; // GObject interface - else if (Proxy.class.isAssignableFrom(cls)) + else if (Proxy.class.isAssignableFrom(type)) return ParamSpecObject.class; - GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, - "Invalid property type %s in method %s in class %s\n", - cls.getName(), - method.getName(), - method.getDeclaringClass().getName()); - return null; + throw new IllegalArgumentException("Invalid property type " + type.getSimpleName()); } /* @@ -340,8 +358,8 @@ else if (pClass.equals(ParamSpecUnichar.class)) return GObjects.paramSpecUnichar(name, name, name, 0, flags); - else - return null; + throw new IllegalArgumentException( + "Unsupported property type: " + pClass.getSimpleName()); } /* @@ -358,146 +376,168 @@ private static Set getFlags(Property property) { return flags; } - /** - * If the provided class defines {@code @Property}-annotated getter and/or - * setter methods, this function will return a class initializer that - * registers these properties as GObject properties and overrides the - * {@code GObject.getProperty} and {@code setProperty} methods to call the - * annotated getters and setters. - * - * @param cls the class that possibly contains @Property annotations - * @return a class initializer that registers the properties + /* + * Check if a `@Property` annotation with `skip=true`is set on this method */ - public static Consumer installProperties(Class cls) { - - List propertySpecs = new ArrayList<>(); - propertySpecs.add(null); // Index 0 is reserved - - /* - * Create an index of property names. The list is used to obtain a - * property id using `list.indexOf(property.name())` - */ - List propertyNames = new ArrayList<>(); - propertyNames.add(null); // index 0 is reserved - - for (Method method : cls.getDeclaredMethods()) { + private static boolean skip(Method method) { + return method.isAnnotationPresent(Property.class) + && method.getAnnotation(Property.class).skip(); + } - // Look for methods with annotation @Property - Property p = method.getAnnotation(Property.class); - if (p == null) - continue; + private final Map names; + private final Map getters; + private final Map setters; + private final Map paramSpecs; + private int index = 0; + + public Properties() { + names = new HashMap<>(); + getters = new HashMap<>(); + setters = new HashMap<>(); + paramSpecs = new HashMap<>(); + } - // Name is specified with the annotation, or infer it from the - // method name - String name = p.name().isEmpty() - ? getPropertyName(method.getName()) - : p.name(); + /* + * Find all property methods: `T getFooBar()`, `void setFooBar(T t)`, and + * methods annotated with `@Property`. The results are put in the hashmaps. + */ + private void inferProperties(Class cls) { + Map possibleGetters = new HashMap<>(); + + // Methods with annotation @Property + for (var method : cls.getDeclaredMethods()) { + if (method.isAnnotationPresent(Property.class)) { + Property p = method.getAnnotation(Property.class); + if (p.skip()) + continue; + + String name = p.name(); + if (name.isBlank()) name = getPropertyName(method.getName()); + + if (names.containsValue(name)) { + for (var entry : names.entrySet()) { + if (entry.getValue().equals(name)) { + int id = entry.getKey(); + if (method.getReturnType().equals(void.class)) + setters.put(id, method); + else + getters.put(id, method); + break; + } + } + } else { + index++; + var flags = getFlags(p); + Class javaType = getJavaType(method); + var paramSpecClass = inferType(javaType); + var paramSpec = createParamSpec(paramSpecClass, name, flags); + paramSpecs.put(index, paramSpec); + names.put(index, name); + if (method.getReturnType().equals(void.class)) + setters.put(index, method); + else + getters.put(index, method); + } + } + } - // Check if this property has already been added from another method - if (propertyNames.contains(name)) + // Getter methods (`T getFoo()` or `boolean isFoo()`) + for (var method : cls.getDeclaredMethods()) { + if (method.isAnnotationPresent(Property.class)) continue; - Class paramSpecClass = p.type(); - Set flags = getFlags(p); - - /* - * Check if the type is set on this method. It can be set on either - * the getter or setter. If the type is not set, it defaults to - * ParamSpec.class - */ - if (paramSpecClass.equals(ParamSpec.class)) - paramSpecClass = inferType(method); + if (isGetter(method)) + possibleGetters.put(getPropertyName(method.getName()), method); + } - if (paramSpecClass == null) + // Setter methods (`void setFoo(T t)`) for which a corresponding + // getter method was found + for (var method : cls.getDeclaredMethods()) { + if (method.isAnnotationPresent(Property.class)) continue; - ParamSpec ps = createParamSpec(paramSpecClass, name, flags); - - if (ps == null) { - GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, - "Unsupported ParamSpec %s in class %s:\n", - paramSpecClass.getName(), cls.getName()); - return null; + if (isSetter(method)) { + String name = getPropertyName(method.getName()); + if (possibleGetters.containsKey(name)) { + Method getter = possibleGetters.get(name); + + // Check that the getter and setter have the same type + if (!getJavaType(getter).equals(getJavaType(method))) + continue; + + // Prevent multiple properties with the same name + if (names.containsValue(name)) + continue; + + index++; + names.put(index, name); + getters.put(index, getter); + setters.put(index, method); + Class javaType = getJavaType(method); + Class paramSpecClass = inferType(javaType); + Set flags = EnumSet.of(ParamFlags.READABLE, ParamFlags.WRITABLE); + ParamSpec paramSpec = createParamSpec(paramSpecClass, name, flags); + paramSpecs.put(index, paramSpec); + } } - - propertySpecs.add(ps); - propertyNames.add(name); } + } - // No properties found? - if (propertySpecs.size() == 1) + /** + * If the provided class defines {@code @Property}-annotated getter and/or + * setter methods, this function will return a class initializer that + * registers these properties as GObject properties and overrides the + * {@code GObject.getProperty} and {@code setProperty} methods to call the + * annotated getters and setters. + * + * @param cls the class that possibly contains @Property annotations + * @return a class initializer that registers the properties + */ + public Consumer installProperties(Class cls) { + inferProperties(cls); + if (index == 0) return null; - // Create arrays of getter and setter methods. - Method[] getters = new Method[propertySpecs.size()]; - Method[] setters = new Method[propertySpecs.size()]; - - for (Method method : cls.getDeclaredMethods()) { - if (! method.isAnnotationPresent(Property.class)) { - continue; - } - Property property = method.getDeclaredAnnotation(Property.class); - - // Name is specified with the annotation, or infer it form the - // method name - String name = property.name().isEmpty() - ? getPropertyName(method.getName()) - : property.name(); - - int idx = propertyNames.indexOf(name); - - // Returns void -> setter, else -> getter - if (method.getReturnType().equals(void.class)) - setters[idx] = method; - else - getters[idx] = method; - } - - // Create GParamSpec array. Index 0 is reserved. - final ParamSpec[] pspecs = new ParamSpec[propertySpecs.size()]; - for (int i = 1; i < propertySpecs.size(); i++) { - pspecs[i] = propertySpecs.get(i); - } - // Return class initializer method that installs the properties. return (gclass) -> { // Override the get_property virtual method overrideGetProperty(gclass, (object, propertyId, value, _) -> { // Check for invalid property IDs - if (propertyId < 1 || propertyId >= getters.length) { + if (propertyId < 1 || propertyId > index) { GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, "Invalid property id %d in %s.getProperty\n", - propertyId, cls.getName()); + propertyId, cls.getSimpleName()); return; } - // Check for non-existing getter method - if (getters[propertyId] == null) { + String name = names.get(propertyId); + if (name == null) name = ""; + Method getter = getters.get(propertyId); + + if (getter == null) { GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, - "No getter method defined for property \"%s\" in %s\n", - propertyNames.get(propertyId), cls.getName()); + "No getter method defined for property with ID=%d and name='%s' in %s\n", + propertyId, name, cls.getSimpleName()); return; } // Invoke the getter method Object output; try { - output = getters[propertyId].invoke(object); + output = getter.invoke(object); } catch (InvocationTargetException e) { // Log exceptions thrown by the getter method Throwable t = e.getTargetException(); GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, "%s.getProperty('%s'): %s\n", - cls.getName(), - propertyNames.get(propertyId), - t.toString()); + cls.getSimpleName(), name, t.toString()); return; } catch (IllegalAccessException e) { // Tried to call a private method GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, "IllegalAccessException calling %s.getProperty('%s')\n", - cls.getName(), propertyNames.get(propertyId)); + cls.getSimpleName(), name); return; } @@ -510,18 +550,21 @@ public static Consumer installProperties(Class cls) { overrideSetProperty(gclass, (object, propertyId, value, _) -> { // Check for invalid property IDs - if (propertyId < 1 || propertyId >= setters.length) { + if (propertyId < 1 || propertyId > index) { GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, "Invalid property id %d in %s.setProperty\n", - propertyId, cls.getName()); + propertyId, cls.getSimpleName()); return; } - // Check for non-existing getter method - if (setters[propertyId] == null) { + String name = names.get(propertyId); + if (name == null) name = ""; + Method setter = setters.get(propertyId); + + if (setter == null) { GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, - "No setter method defined for property \"%s\" in %s\n", - propertyNames.get(propertyId), cls.getName()); + "No getter method defined for property with ID=%d and name='%s' in %s\n", + propertyId, name, cls.getSimpleName()); return; } @@ -531,35 +574,40 @@ public static Consumer installProperties(Class cls) { // Invoke the setter method if (input != null) { try { - setters[propertyId].invoke(object, input); + setter.invoke(object, input); } catch (InvocationTargetException e) { // Log exceptions thrown by the setter method Throwable t = e.getTargetException(); GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, "%s.setProperty('%s'): %s\n", - cls.getName(), - propertyNames.get(propertyId), - t.toString()); + cls.getSimpleName(), name, t.toString()); } catch (IllegalAccessException e) { // Tried to call a private method GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, "IllegalAccessException calling %s.setProperty('%s')\n", - cls.getName(), propertyNames.get(propertyId)); + cls.getSimpleName(), name); } } }, Arena.global()); // Register properties for the generated ParamSpecs if (gclass instanceof GObject.ObjectClass oclass) { - oclass.installProperties(pspecs); - } else if (cls.isInterface() && Types.isGObjectBased(cls)) { + for (int i = 1; i <= index; i++) { + oclass.installProperty(i, paramSpecs.get(i)); + } + } + + else if (cls.isInterface() && Types.isGObjectBased(cls)) { var giface = new TypeInterface(gclass.handle()); - for (var pspec : pspecs) - GObject.interfaceInstallProperty(giface, pspec); - } else { + for (int i = 1; i <= index; i++) { + GObject.interfaceInstallProperty(giface, paramSpecs.get(i)); + } + } + + else { GLib.log(LOG_DOMAIN, LogLevelFlags.LEVEL_CRITICAL, "Class or interface %s is not based on GObject\n", - cls.getName()); + cls.getSimpleName()); } }; } diff --git a/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/types/Types.java b/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/types/Types.java index 116877ae..d0bf4383 100644 --- a/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/types/Types.java +++ b/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/types/Types.java @@ -1223,7 +1223,7 @@ public static Type register(Class cls) { Consumer signalsInit; if (isGObjectBased(cls)) { signalsInit = Signals.installSignals(cls); - propertiesInit = Properties.installProperties(cls); + propertiesInit = new Properties().installProperties(cls); } else { signalsInit = null; propertiesInit = null; diff --git a/modules/gobject/src/test/java/io/github/jwharm/javagi/test/gobject/PropertyBindingTest.java b/modules/gobject/src/test/java/io/github/jwharm/javagi/test/gobject/PropertyBindingTest.java index 5f81aaeb..b7f50f3c 100644 --- a/modules/gobject/src/test/java/io/github/jwharm/javagi/test/gobject/PropertyBindingTest.java +++ b/modules/gobject/src/test/java/io/github/jwharm/javagi/test/gobject/PropertyBindingTest.java @@ -19,7 +19,7 @@ package io.github.jwharm.javagi.test.gobject; -import io.github.jwharm.javagi.gobject.annotations.Property; +import io.github.jwharm.javagi.gobject.annotations.RegisteredType; import io.github.jwharm.javagi.gobject.types.Types; import org.gnome.glib.Type; import org.gnome.gobject.GObject; @@ -83,6 +83,7 @@ void testArgumentValidation() { assertTrue((boolean) mph.getProperty("too-fast")); } + @RegisteredType(name="Speed") public static class Speed extends GObject { private static final Type type = Types.register(Speed.class); private double currentSpeed = 0.0; @@ -102,22 +103,18 @@ public static Speed create(double currentSpeed) { null); } - @Property public double getCurrentSpeed() { return currentSpeed; } - @Property public void setCurrentSpeed(double currentSpeed) { this.currentSpeed = currentSpeed; } - @Property public boolean getTooFast() { return currentSpeed > limit; } - @Property public void setTooFast(boolean tooFast) { if (tooFast) setProperty("current-speed", limit + 10.0); diff --git a/modules/gobject/src/test/java/io/github/jwharm/javagi/test/gobject/PropertyTest.java b/modules/gobject/src/test/java/io/github/jwharm/javagi/test/gobject/PropertyTest.java new file mode 100644 index 00000000..3803259e --- /dev/null +++ b/modules/gobject/src/test/java/io/github/jwharm/javagi/test/gobject/PropertyTest.java @@ -0,0 +1,78 @@ +package io.github.jwharm.javagi.test.gobject; + +import io.github.jwharm.javagi.gobject.annotations.Property; +import io.github.jwharm.javagi.gobject.annotations.RegisteredType; +import io.github.jwharm.javagi.gobject.types.Types; +import org.gnome.gobject.GObject; +import org.junit.jupiter.api.Test; + +import java.lang.foreign.MemorySegment; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class PropertyTest { + + @Test + void testCustomProperties() { + Types.register(Dino.class); + var dino = GObject.newInstance(Dino.class); + var gclass = (GObject.ObjectClass) dino.readGClass(); + + assertNotNull(gclass.findProperty("foo")); + assertNotNull(gclass.findProperty("bar")); + assertNotNull(gclass.findProperty("baz2")); + + assertNull(gclass.findProperty("baz")); + assertNull(gclass.findProperty("qux")); + } + + @SuppressWarnings("unused") + @RegisteredType(name="Dino") + public static class Dino extends GObject{ + private int foo; + private boolean bar; + private String baz; + private float qux; + + public Dino(MemorySegment address) { + super(address); + } + + public int getFoo() { + return foo; + } + + public void setFoo(int foo) { + this.foo = foo; + } + + public boolean isBar() { + return bar; + } + + public void setBar(boolean bar) { + this.bar = bar; + } + + @Property(name="baz2") + public String readBaz() { + return baz; + } + + @Property(name="baz2") + public void writeBaz(String baz) { + this.baz = baz; + } + + @Property(skip=true) + public float getQux() { + return qux; + } + + @Property(skip=true) + public void setQux(float qux) { + this.qux = qux; + } + } +} diff --git a/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/types/TemplateTypes.java b/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/types/TemplateTypes.java index 6e9ad2cf..1137d077 100644 --- a/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/types/TemplateTypes.java +++ b/modules/gtk/src/main/java/io/github/jwharm/javagi/gtk/types/TemplateTypes.java @@ -292,7 +292,7 @@ private static Type registerTemplate(Class cls) { // Chain template class init with user-defined class init function var overridesInit = Overrides.overrideClassMethods(cls); - var propertiesInit = Properties.installProperties(cls); + var propertiesInit = new Properties().installProperties(cls); var signalsInit = Signals.installSignals(cls); var templateClassInit = getTemplateClassInit(cls, instanceLayout); var userDefinedClassInit = getClassInit(cls); From 1875912d26d587353256b54265d2e39390f22d98 Mon Sep 17 00:00:00 2001 From: Jan-Willem Harmannij Date: Sun, 15 Dec 2024 22:51:57 +0100 Subject: [PATCH 3/4] Add Javadoc to `@Property` annotation --- .../jwharm/javagi/gobject/annotations/Property.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/annotations/Property.java b/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/annotations/Property.java index 5bff4729..5ea04be8 100644 --- a/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/annotations/Property.java +++ b/modules/gobject/src/main/java/io/github/jwharm/javagi/gobject/annotations/Property.java @@ -1,5 +1,5 @@ /* Java-GI - Java language bindings for GObject-Introspection-based libraries - * Copyright (C) 2022-2023 Jan-Willem Harmannij + * Copyright (C) 2022-2024 Jan-Willem Harmannij * * SPDX-License-Identifier: LGPL-2.1-or-later * @@ -26,6 +26,15 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * The {@code @Property} annotation is used to indicate that a method (or pair + * of methods) is a property, to set a property name and flags, or to specify + * that a pair of get- and set-methods are not properties (using + * {@code skip=false}). + *

+ * Always set the {@code @Property} annotation with the same parameters on both + * the get- and set-method. + */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Property { @@ -38,6 +47,4 @@ boolean constructOnly() default false; boolean explicitNotify() default false; boolean deprecated() default false; - - } From eecbc55062883f15677e55871a2abb6de6bee13e Mon Sep 17 00:00:00 2001 From: Jan-Willem Harmannij Date: Sun, 15 Dec 2024 23:00:49 +0100 Subject: [PATCH 4/4] Small improvement of the annotations in ListIndexModel --- .../java/io/github/jwharm/javagi/gio/ListIndexModel.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/gio/src/main/java/io/github/jwharm/javagi/gio/ListIndexModel.java b/modules/gio/src/main/java/io/github/jwharm/javagi/gio/ListIndexModel.java index 1454e4f0..8e8b1288 100644 --- a/modules/gio/src/main/java/io/github/jwharm/javagi/gio/ListIndexModel.java +++ b/modules/gio/src/main/java/io/github/jwharm/javagi/gio/ListIndexModel.java @@ -87,7 +87,7 @@ public void setSize(int size) { * * @return always returns the value of {@link ListIndex#gtype} */ - @Property(name="item-type", constructOnly = true) + @Property(constructOnly = true) @Override public Type getItemType() { return ListIndex.gtype; @@ -98,7 +98,8 @@ public Type getItemType() { * * @param itemType ignored */ - @Property(name="item-type") + @SuppressWarnings("unused") + @Property(constructOnly = true) public void setItemType(Type itemType) { }