@@ -186,7 +186,7 @@ private Map<File, String> extractAll(Element _ife) throws IOException, DBusExcep
186186 switch (element .getTagName ().toLowerCase ()) {
187187 case "method" -> additionalClasses .addAll (extractMethods (element , interfaceClass ));
188188 case "property" -> additionalClasses .addAll (extractProperties (element , interfaceClass ));
189- case "signal" -> additionalClasses . addAll ( extractSignals (element , interfaceClass ) );
189+ case "signal" -> extractSignals (element , interfaceClass );
190190 }
191191 }
192192
@@ -203,18 +203,11 @@ private Map<File, String> extractAll(Element _ife) throws IOException, DBusExcep
203203 *
204204 * @param _signalElement signal xml element
205205 * @param _clzBldr {@link ClassBuilderInfo} object
206- * @return List of {@link ClassBuilderInfo} which have been created (maybe empty, never null)
207206 *
208207 * @throws IOException on IO Error
209208 * @throws DBusException on DBus Error
210209 */
211- private List <ClassBuilderInfo > extractSignals (Element _signalElement , ClassBuilderInfo _clzBldr ) throws IOException , DBusException {
212- List <ClassBuilderInfo > additionalClasses = new ArrayList <>();
213-
214- if (!_signalElement .hasChildNodes ()) { // signal without any input/output?!
215- logger .warn ("Signal without any input/output arguments. These are not supported yet, please open a ticket at github!" );
216- return additionalClasses ;
217- }
210+ private void extractSignals (Element _signalElement , ClassBuilderInfo _clzBldr ) throws IOException , DBusException {
218211
219212 String className = _signalElement .getAttribute ("name" );
220213 if (className .contains ("." )) {
@@ -229,42 +222,42 @@ private List<ClassBuilderInfo> extractSignals(Element _signalElement, ClassBuild
229222 innerClass .setClassName (className );
230223
231224 _clzBldr .getInnerClasses ().add (innerClass );
225+ List <MemberOrArgument > argsList = new ArrayList <>();
226+
227+ if (!_signalElement .hasChildNodes ()) { // signal without any input/output?!
228+ logger .info ("Signal without any input/output arguments. Creating empty signal class: {}" , innerClass .getFqcn ());
229+ } else {
230+ List <Element > signalArgs = XmlUtil .convertToElementList (XmlUtil .applyXpathExpressionToDocument ("arg" , _signalElement ));
232231
233- List < Element > signalArgs = XmlUtil . convertToElementList ( XmlUtil . applyXpathExpressionToDocument ( "arg" , _signalElement ) );
232+ Map < String , String > args = new LinkedHashMap <>( );
234233
235- Map <String , String > args = new LinkedHashMap <>();
234+ int unknownArgCnt = 0 ;
235+ for (Element argElm : signalArgs ) {
236+ String argType = TypeConverter .getJavaTypeFromDBusType (argElm .getAttribute ("type" ), _clzBldr .getImports ());
237+ String argName = Util .snakeToCamelCase (argElm .getAttribute ("name" ));
238+ if (Util .isBlank (argName )) {
239+ argName = "arg" + unknownArgCnt ;
240+ unknownArgCnt ++;
241+ }
242+ args .put (argName , TypeConverter .getProperJavaClass (argType , _clzBldr .getImports ()));
243+ }
236244
237- int unknownArgCnt = 0 ;
238- for (Element argElm : signalArgs ) {
239- String argType = TypeConverter .getJavaTypeFromDBusType (argElm .getAttribute ("type" ), _clzBldr .getImports ());
240- String argName = Util .snakeToCamelCase (argElm .getAttribute ("name" ));
241- if (Util .isBlank (argName )) {
242- argName = "arg" + unknownArgCnt ;
243- unknownArgCnt ++;
245+ for (Entry <String , String > argEntry : args .entrySet ()) {
246+ innerClass .getMembers ().add (new MemberOrArgument (argEntry .getKey (), argEntry .getValue (), true ));
247+ argsList .add (new MemberOrArgument (argEntry .getKey (), argEntry .getValue (), false ));
244248 }
245- args .put (argName , TypeConverter .getProperJavaClass (argType , _clzBldr .getImports ()));
246- }
247249
248- for (Entry <String , String > argEntry : args .entrySet ()) {
249- innerClass .getMembers ().add (new MemberOrArgument (argEntry .getKey (), argEntry .getValue (), true ));
250250 }
251251
252252 ClassConstructor classConstructor = new ClassBuilderInfo .ClassConstructor ();
253253
254- List <MemberOrArgument > argsList = new ArrayList <>();
255- for (Entry <String , String > e : args .entrySet ()) {
256- argsList .add (new MemberOrArgument ("_" + e .getKey (), e .getValue (), false ));
257- }
258-
259254 classConstructor .getArguments ().addAll (argsList );
260255 classConstructor .getThrowArguments ().add (DBusException .class .getSimpleName ());
261256
262- classConstructor .getSuperArguments ().add (new MemberOrArgument ("_path " , "String" , false ));
257+ classConstructor .getSuperArguments ().add (new MemberOrArgument ("path " , "String" , false ));
263258 classConstructor .getSuperArguments ().addAll (argsList );
264259
265260 innerClass .getConstructors ().add (classConstructor );
266-
267- return additionalClasses ;
268261 }
269262
270263 /**
@@ -327,7 +320,22 @@ private List<ClassBuilderInfo> extractMethods(Element _methodElement, ClassBuild
327320 String resultType ;
328321 if (outputArgs .size () > 1 ) { // multi-value return
329322 logger .debug ("Found method with multiple return values: {}" , methodElementName );
330- resultType = createTuple (outputArgs , methodElementName + "Tuple" , _clzBldr , additionalClasses , dbusOutputArgTypes );
323+ List <String > genericTypes = new ArrayList <>();
324+
325+ resultType = createTuple (outputArgs , methodElementName + "Tuple" , _clzBldr , additionalClasses , dbusOutputArgTypes , genericTypes );
326+
327+ genericTypes .stream ()
328+ .flatMap (e -> ClassBuilderInfo .getImportsForType (e ).stream ())
329+ .forEach (_clzBldr .getImports ()::add );
330+
331+ _clzBldr .getImports ().add (resultType );
332+
333+ resultType = Util .extractClassNameFromFqcn (resultType );
334+ List <String > returnGenerics = genericTypes .stream ()
335+ .map (TypeConverter ::convertJavaPrimitiveToBoxed )
336+ .map (ClassBuilderInfo ::getSimpleTypeClasses )
337+ .toList ();
338+ resultType += "<" + String .join (", " , returnGenerics ) + ">" ;
331339 } else {
332340 logger .debug ("Found method with arguments: {}({})" , methodElementName , inputArgs );
333341 resultType = outputArgs .isEmpty () ? "void" : outputArgs .get (0 ).getFullType (new HashSet <>());
@@ -401,7 +409,7 @@ private List<ClassBuilderInfo> extractProperties(Element _propertyElement, Class
401409 ClassBuilderInfo propertyTypeRef = null ;
402410 String origType = null ;
403411 if (!isComplex ) {
404- clzzName = ClassBuilderInfo . getClassName (type );
412+ clzzName = Util . extractClassNameFromFqcn (type );
405413 } else {
406414 origType = type ;
407415 type = TypeRef .class .getName () + "<" + type + ">" ;
@@ -464,7 +472,8 @@ private List<ClassBuilderInfo> extractProperties(Element _propertyElement, Class
464472 * @param _dbusOutputArgTypes Dbus argument names and data types
465473 * @return FQCN of the newly created tuple based class
466474 */
467- private String createTuple (List <MemberOrArgument > _outputArgs , String _className , ClassBuilderInfo _parentClzBldr , List <ClassBuilderInfo > _additionalClasses , List <String > _dbusOutputArgTypes ) {
475+ private String createTuple (List <MemberOrArgument > _outputArgs , String _className ,
476+ ClassBuilderInfo _parentClzBldr , List <ClassBuilderInfo > _additionalClasses , List <String > _dbusOutputArgTypes , List <String > _genericTypes ) {
468477 if (_outputArgs == null || _outputArgs .isEmpty () || _additionalClasses == null ) {
469478 return null ;
470479 }
@@ -478,29 +487,48 @@ private String createTuple(List<MemberOrArgument> _outputArgs, String _className
478487 info .getImports ().add (Position .class .getName ());
479488 }
480489
481- ArrayList <MemberOrArgument > cnstrctArgs = new ArrayList <>();
490+ List <MemberOrArgument > cnstrctArgs = new ArrayList <>();
491+ Map <String , String > genericTypes = new LinkedHashMap <>();
492+
482493 int position = 0 ;
483494 for (MemberOrArgument entry : _outputArgs ) {
484- entry .getAnnotations ().add ("@Position(" + position ++ + ")" );
485- cnstrctArgs .add (new MemberOrArgument (entry .getName (), entry .getType ()));
486- }
495+ String genericName = findNextGenericName (genericTypes .keySet ());
487496
488- for ( String outputType : _dbusOutputArgTypes ) {
489- for ( String part : outputType . replace ( " " , "" ). replace ( ">" , "" ). split ( "<|," )) {
490- info . getImports (). add ( part );
491- }
497+ genericTypes . put ( genericName , entry . getType ());
498+ entry . getAnnotations (). add ( "@Position(" + position ++ + ")" );
499+ entry . setType ( genericName );
500+ cnstrctArgs . add ( new MemberOrArgument ( entry . getName (), genericName ));
492501 }
493502
494503 ClassConstructor cnstrct = new ClassConstructor ();
495504 cnstrct .getArguments ().addAll (cnstrctArgs );
496505
497506 info .getConstructors ().add (cnstrct );
498507 info .getMembers ().addAll (_outputArgs );
508+ info .getGenerics ().addAll (genericTypes .keySet ());
509+
499510 _additionalClasses .add (info );
511+ _genericTypes .addAll (genericTypes .values ());
500512
501513 return info .getFqcn ();
502514 }
503515
516+ /**
517+ * Tries to find next available generic name for a Tuple class.
518+ * @param _used Set of already used names
519+ * @return next available name
520+ * @throws IllegalStateException if no name could be found
521+ */
522+ static String findNextGenericName (Set <String > _used ) {
523+ for (char c = 'A' ; c <= 'Z' ; c ++) {
524+ String name = String .valueOf (c );
525+ if (!_used .contains (name )) {
526+ return name ;
527+ }
528+ }
529+ throw new IllegalStateException ("Unable to find a generic name for Tuple class" );
530+ }
531+
504532 /**
505533 * Creates a class for a DBus Struct-Object.
506534 *
@@ -514,14 +542,8 @@ private String createTuple(List<MemberOrArgument> _outputArgs, String _className
514542 */
515543 private String buildStructClass (String _dbusTypeStr , String _structName , ClassBuilderInfo _packageName , List <ClassBuilderInfo > _structClasses ) throws DBusException {
516544 String structFqcn = _packageName .getPackageName () + "." + Util .upperCaseFirstChar (_structName );
517- String structName = _structName ;
518- if (generatedStructClassNames .contains (structFqcn )) {
519- while (generatedStructClassNames .contains (structFqcn )) {
520- structFqcn += "Struct" ;
521- structName += "Struct" ;
522- }
523- }
524- String structClassName = new StructTreeBuilder (argumentPrefix ).buildStructClasses (_dbusTypeStr , structName , _packageName , _structClasses );
545+ String structClassName = new StructTreeBuilder (argumentPrefix , generatedStructClassNames )
546+ .buildStructClasses (_dbusTypeStr , structFqcn , _packageName , _structClasses );
525547 generatedStructClassNames .add (structFqcn );
526548 return structClassName ;
527549 }
0 commit comments