001/* This file is part of Vault.
002
003    Vault is free software: you can redistribute it and/or modify
004    it under the terms of the GNU Lesser General Public License as published by
005    the Free Software Foundation, either version 3 of the License, or
006    (at your option) any later version.
007
008    Vault is distributed in the hope that it will be useful,
009    but WITHOUT ANY WARRANTY; without even the implied warranty of
010    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
011    GNU Lesser General Public License for more details.
012
013    You should have received a copy of the GNU Lesser General Public License
014    along with Vault.  If not, see <http://www.gnu.org/licenses/>.
015 */
016package net.milkbowl.vault.permission;
017
018import java.util.logging.Logger;
019
020import org.bukkit.OfflinePlayer;
021import org.bukkit.World;
022import org.bukkit.command.CommandSender;
023import org.bukkit.entity.Player;
024import org.bukkit.permissions.PermissionAttachment;
025import org.bukkit.permissions.PermissionAttachmentInfo;
026import org.bukkit.plugin.Plugin;
027
028/**
029 * The main Permission API - allows for group and player based permission tests
030 *
031 */
032public abstract class Permission {
033
034        protected static final Logger log = Logger.getLogger("Minecraft");
035    protected Plugin plugin = null;
036
037    /**
038     * Gets name of permission method
039     * @return Name of Permission Method
040     */
041    abstract public String getName();
042
043    /**
044     * Checks if permission method is enabled.
045     * @return Success or Failure
046     */
047    abstract public boolean isEnabled();
048    
049    /**
050     * Returns if the permission system is or attempts to be compatible with super-perms.
051     * @return True if this permission implementation works with super-perms
052     */
053    abstract public boolean hasSuperPermsCompat();
054    
055    /**
056     * @deprecated As of VaultAPI 1.4 use {@link #playerHas(String, OfflinePlayer, String)} instead.
057     */
058    @Deprecated
059    public boolean has(String world, String player, String permission) {
060        if (world == null) {
061            return playerHas((String) null, player, permission);
062        }
063        return playerHas(world, player, permission);
064    }
065
066    /**
067     * @deprecated As of VaultAPI 1.4 use {@link #playerHas(String, OfflinePlayer, String)} instead.
068     */
069    @Deprecated
070    public boolean has(World world, String player, String permission) {
071        if (world == null) {
072            return playerHas((String) null, player, permission);
073        }
074        return playerHas(world.getName(), player, permission);
075    }
076
077    /**
078     * Checks if a CommandSender has a permission node.
079     * This will return the result of bukkits, generic .hasPermission() method and is identical in all cases.
080     * This method will explicitly fail if the registered permission system does not register permissions in bukkit.
081     * 
082     * For easy checking of a commandsender
083     * @param sender to check permissions on
084     * @param permission to check for
085     * @return true if the sender has the permission
086     */
087    public boolean has(CommandSender sender, String permission) {
088        return sender.hasPermission(permission);
089    }
090
091    /**
092     * Checks if player has a permission node. (Short for playerHas(...)
093     * @param player Player Object
094     * @param permission Permission node
095     * @return Success or Failure
096     */
097    public boolean has(Player player, String permission) {
098        return player.hasPermission(permission);
099    }
100
101    /**
102     * @deprecated As of VaultAPI 1.4 use {@link #playerHas(String, OfflinePlayer, String)} instead.
103     */
104    @Deprecated
105    abstract public boolean playerHas(String world, String player, String permission);
106
107    /**
108     * @deprecated As of VaultAPI 1.4 use {@link #playerHas(String, OfflinePlayer, String)} instead.
109     */
110    @Deprecated
111    public boolean playerHas(World world, String player, String permission) {
112        if (world == null) {
113            return playerHas((String) null, player, permission);
114        }
115        return playerHas(world.getName(), player, permission);
116    }
117    
118    /**
119     * Checks if player has a permission node.
120     * Supports NULL value for World if the permission system registered supports global permissions.
121     * But May return odd values if the servers registered permission system does not have a global permission store.
122     * 
123     * @param world String world name
124     * @param player to check
125     * @param permission Permission node
126     * @return Success or Failure
127     */
128    public boolean playerHas(String world, OfflinePlayer player, String permission) {
129        if (world == null) {
130                return has((String) null, player.getName(), permission);
131        }
132        return has(world, player.getName(), permission);
133    }
134
135    /**
136     * Checks if player has a permission node.
137     * Defaults to world-specific permission check if the permission system supports it.
138     * See {@link #playerHas(String, OfflinePlayer, String)} for explicit global or world checks.
139     *  
140     * @param player Player Object
141     * @param permission Permission node
142     * @return Success or Failure
143     */
144    public boolean playerHas(Player player, String permission) {
145        return has(player, permission);
146    }
147
148    /**
149     * @deprecated As of VaultAPI 1.4 use {@link #playerAdd(String, OfflinePlayer, String)} instead.
150     * Add permission to a player.
151     * Supports NULL value for World if the permission system registered supports global permissions.
152     * But May return odd values if the servers registered permission system does not have a global permission store.
153     * 
154     * @param world World name
155     * @param player Player name
156     * @param permission Permission node
157     * @return Success or Failure
158     */
159    @Deprecated
160    abstract public boolean playerAdd(String world, String player, String permission);
161
162    /**
163     * @deprecated As of VaultAPI 1.4 use {@link #playerAdd(String, OfflinePlayer, String)} instead.
164     */
165    @Deprecated
166    public boolean playerAdd(World world, String player, String permission) {
167        if (world == null) {
168            return playerAdd((String) null, player, permission);
169        }
170        return playerAdd(world.getName(), player, permission);
171    }
172
173    /**
174     * Add permission to a player.
175     * Supports NULL value for World if the permission system registered supports global permissions.
176     * But May return odd values if the servers registered permission system does not have a global permission store.
177     * 
178     * @param world String world name
179     * @param player to add to
180     * @param permission Permission node
181     * @return Success or Failure
182     */
183    public boolean playerAdd(String world, OfflinePlayer player, String permission) {
184        if (world == null) {
185            return playerAdd((String) null, player.getName(), permission);
186        }
187        return playerAdd(world, player.getName(), permission);
188    }
189
190    /**
191     * Add permission to a player ONLY for the world the player is currently on.
192     * This is a world-specific operation, if you want to add global permission you must explicitly use NULL for the world.
193     * See {@link #playerAdd(String, OfflinePlayer, String)} for global permission use.
194     * 
195     * @param player Player Object
196     * @param permission Permission node
197     * @return Success or Failure
198     */
199    public boolean playerAdd(Player player, String permission) {
200        return playerAdd(player.getWorld().getName(), player, permission);
201    }
202    
203    /**
204     * @deprecated As of VaultAPI 1.4 use {@link #playerAddTransient(OfflinePlayer, String)} instead.
205     */
206    @Deprecated
207    public boolean playerAddTransient(String player, String permission) throws UnsupportedOperationException {
208                Player p = plugin.getServer().getPlayer(player);
209                if (p == null) {
210                        throw new UnsupportedOperationException(getName() + " does not support offline player transient permissions!");
211                }
212                return playerAddTransient(p, permission);
213        }
214    
215    /**
216     * Add transient permission to a player.
217     * This implementation can be used by any subclass which implements a "pure" superperms plugin, i.e. 
218     * one that only needs the built-in Bukkit API to add transient permissions to a player.
219     * 
220     * @param player to add to
221     * @param permission Permission node
222     * @return Success or Failure
223     */
224    public boolean playerAddTransient(OfflinePlayer player, String permission) throws UnsupportedOperationException {
225        if (player.isOnline()) {
226                return playerAddTransient((Player) player, permission);
227        }
228                throw new UnsupportedOperationException(getName() + " does not support offline player transient permissions!");
229        }
230
231    /**
232     * Add transient permission to a player.
233     * This operation adds a permission onto the player object in bukkit via Bukkit's permission interface.
234     * 
235     * @param player Player Object
236     * @param permission Permission node
237     * @return Success or Failure
238     */
239    public boolean playerAddTransient(Player player, String permission) {
240                for (PermissionAttachmentInfo paInfo : player.getEffectivePermissions()) {
241                        if (paInfo.getAttachment() != null && paInfo.getAttachment().getPlugin().equals(plugin)) {
242                                paInfo.getAttachment().setPermission(permission, true);
243                                return true;
244                        }
245                }
246
247                PermissionAttachment attach = player.addAttachment(plugin);
248                attach.setPermission(permission, true);
249
250                return true;
251    }
252
253    /**
254     * Adds a world specific transient permission to the player, may only work with some permission managers.
255     * Defaults to GLOBAL permissions for any permission system that does not support world-specific transient permissions!
256     * 
257     * @param worldName to check on
258     * @param player to add to
259     * @param permission to test
260     * @return Success or Failure
261     */
262    public boolean playerAddTransient(String worldName, OfflinePlayer player, String permission) {
263        return playerAddTransient(worldName, player.getName(), permission);
264    }
265    
266    /**
267     * Adds a world specific transient permission to the player, may only work with some permission managers.
268     * Defaults to GLOBAL permissions for any permission system that does not support world-specific transient permissions!
269     * 
270     * @param worldName to check on
271     * @param player to check
272     * @param permission to check for
273     * @return Success or Failure
274     */
275    public boolean playerAddTransient(String worldName, Player player, String permission) {
276        return playerAddTransient(player, permission);
277    }
278    
279    /**
280     * @deprecated As of VaultAPI 1.4 use {@link #playerAddTransient(String, OfflinePlayer, String)} instead.
281     */
282    @Deprecated
283    public boolean playerAddTransient(String worldName, String player, String permission) {
284                Player p = plugin.getServer().getPlayer(player);
285                if (p == null) {
286                        throw new UnsupportedOperationException(getName() + " does not support offline player transient permissions!");
287                }
288                return playerAddTransient(p, permission);
289    }
290    
291    /**
292     * @deprecated As of VaultAPI 1.4 use {@link #playerRemoveTransient(String, OfflinePlayer, String)} instead.
293     */
294    @Deprecated
295        public boolean playerRemoveTransient(String worldName, String player, String permission) {
296                Player p = plugin.getServer().getPlayer(player);
297                if (p == null)
298                        return false;
299                
300                return playerRemoveTransient(p, permission);
301        }
302        
303    /**
304     * Removes a world specific transient permission from the player, may only work with some permission managers.
305     * Defaults to GLOBAL permissions for any permission system that does not support world-specific transient permissions!
306     * 
307     * @param worldName to remove for
308     * @param player to remove for
309     * @param permission to remove
310     * @return Success or Failure
311     */
312    public boolean playerRemoveTransient(String worldName, OfflinePlayer player, String permission) {
313        return playerRemoveTransient(worldName, player.getName(), permission);
314    }
315        
316    /**
317     * Removes a world specific transient permission from the player, may only work with some permission managers.
318     * Defaults to GLOBAL permissions for any permission system that does not support world-specific transient permissions!
319     * 
320     * @param worldName to check on
321     * @param player to check
322     * @param permission to check for
323     * @return Success or Failure
324     */
325    public boolean playerRemoveTransient(String worldName, Player player, String permission) {
326        return playerRemoveTransient(worldName, (OfflinePlayer) player, permission);
327    }
328    
329    /**
330     * @deprecated As of VaultAPI 1.4 use {@link #playerRemove(String, OfflinePlayer, String)} instead.
331     */
332    @Deprecated
333    abstract public boolean playerRemove(String world, String player, String permission);
334
335    /**
336     * Remove permission from a player.
337     * Supports NULL value for World if the permission system registered supports global permissions.
338     * But May return odd values if the servers registered permission system does not have a global permission store.
339     * 
340     * @param world World name
341     * @param player OfflinePlayer
342     * @param permission Permission node
343     * @return Success or Failure
344     */
345    public boolean playerRemove(String world, OfflinePlayer player, String permission) {
346        if (world == null) {
347            return playerRemove((String) null, player.getName(), permission);
348        }
349        return playerRemove(world, player.getName(), permission);
350    }
351
352    /**
353     * Remove permission from a player.
354     * Supports NULL value for World if the permission system registered supports global permissions.
355     * But May return odd values if the servers registered permission system does not have a global permission store.
356     * 
357     * @param world World name
358     * @param player Player name
359     * @param permission Permission node
360     * @return Success or Failure
361     */
362    @Deprecated
363    public boolean playerRemove(World world, String player, String permission) {
364        if (world == null) {
365            return playerRemove((String) null, player, permission);
366        }
367        return playerRemove(world.getName(), player, permission);
368    }
369
370    /**
371     * Remove permission from a player.
372     * Will attempt to remove permission from the player on the player's current world.  This is NOT a global operation.
373     * 
374     * @param player Player Object
375     * @param permission Permission node
376     * @return Success or Failure
377     */
378    public boolean playerRemove(Player player, String permission) {
379        return playerRemove(player.getWorld().getName(), player, permission);
380    }
381    
382    /**
383     * @deprecated As of VaultAPI 1.4 use {@link #playerRemoveTransient(OfflinePlayer, String)} instead.
384     */
385    @Deprecated
386        public boolean playerRemoveTransient(String player, String permission) {
387                Player p = plugin.getServer().getPlayer(player);
388                if (p == null)
389                        return false;
390                
391                return playerRemoveTransient(p, permission);
392        }
393
394    /**
395     * Remove transient permission from a player.
396     * This implementation can be used by any subclass which implements a "pure" superperms plugin, i.e. 
397     * one that only needs the built-in Bukkit API to remove transient permissions from a player.  Any subclass
398     * implementing a plugin which provides its own API for this needs to override this method.
399     * 
400     * @param player OfflinePlayer
401     * @param permission Permission node
402     * @return Success or Failure
403     */
404        public boolean playerRemoveTransient(OfflinePlayer player, String permission) {
405                if (player.isOnline()) {
406                        return playerRemoveTransient((Player) player, permission);
407                } else {
408                        return false;
409                }
410        }
411    
412    /**
413     * Remove transient permission from a player.
414     * 
415     * @param player Player Object
416     * @param permission Permission node
417     * @return Success or Failure
418     */
419    public boolean playerRemoveTransient(Player player, String permission) {
420                for (PermissionAttachmentInfo paInfo : player.getEffectivePermissions()) {
421                        if (paInfo.getAttachment() != null && paInfo.getAttachment().getPlugin().equals(plugin)) {
422                                paInfo.getAttachment().unsetPermission(permission);
423                                return true;
424                        }
425                }
426                return false;
427    }
428    
429    /**
430     * Checks if group has a permission node.
431     * Supports NULL value for World if the permission system registered supports global permissions.
432     * But May return odd values if the servers registered permission system does not have a global permission store.
433     * 
434     * @param world World name
435     * @param group Group name
436     * @param permission Permission node
437     * @return Success or Failure
438     */
439    abstract public boolean groupHas(String world, String group, String permission);
440
441    /**
442     * Checks if group has a permission node.
443     * Supports NULL value for World if the permission system registered supports global permissions.
444     * But May return odd values if the servers registered permission system does not have a global permission store.
445     * 
446     * @param world World Object
447     * @param group Group name
448     * @param permission Permission node
449     * @return Success or Failure
450     */
451    public boolean groupHas(World world, String group, String permission) {
452        if (world == null) {
453            return groupHas((String) null, group, permission);
454        }
455        return groupHas(world.getName(), group, permission);
456    }
457
458    /**
459     * Add permission to a group.
460     * Supports NULL value for World if the permission system registered supports global permissions.
461     * But May return odd values if the servers registered permission system does not have a global permission store.
462     * 
463     * @param world World name
464     * @param group Group name
465     * @param permission Permission node
466     * @return Success or Failure
467     */
468    abstract public boolean groupAdd(String world, String group, String permission);
469
470    /**
471     * Add permission to a group.
472     * Supports NULL value for World if the permission system registered supports global permissions.
473     * But May return odd values if the servers registered permission system does not have a global permission store.
474     * 
475     * @param world World Object
476     * @param group Group name
477     * @param permission Permission node
478     * @return Success or Failure
479     */
480    public boolean groupAdd(World world, String group, String permission) {
481        if (world == null) {
482            return groupAdd((String) null, group, permission);
483        }
484        return groupAdd(world.getName(), group, permission);
485    }
486
487    /**
488     * Remove permission from a group.
489     * Supports NULL value for World if the permission system registered supports global permissions.
490     * But May return odd values if the servers registered permission system does not have a global permission store.
491     * 
492     * @param world World name
493     * @param group Group name
494     * @param permission Permission node
495     * @return Success or Failure
496     */
497    abstract public boolean groupRemove(String world, String group, String permission);
498
499    /**
500     * Remove permission from a group.
501     * Supports NULL value for World if the permission system registered supports global permissions.
502     * But May return odd values if the servers registered permission system does not have a global permission store.
503     * 
504     * @param world World Object
505     * @param group Group name
506     * @param permission Permission node
507     * @return Success or Failure
508     */
509    public boolean groupRemove(World world, String group, String permission) {
510        if (world == null) {
511            return groupRemove((String) null, group, permission);
512        }
513        return groupRemove(world.getName(), group, permission);
514    }
515
516    /**
517     * @deprecated As of VaultAPI 1.4 use {@link #playerInGroup(String, OfflinePlayer, String)} instead.
518     */
519    @Deprecated
520    abstract public boolean playerInGroup(String world, String player, String group);
521
522    /**
523     * @deprecated As of VaultAPI 1.4 use {@link #playerInGroup(String, OfflinePlayer, String)} instead.
524     */
525    @Deprecated
526    public boolean playerInGroup(World world, String player, String group) {
527        if (world == null) {
528            return playerInGroup((String) null, player, group);
529        }
530        return playerInGroup(world.getName(), player, group);
531    }
532    
533    /**
534     * Check if player is member of a group.
535     * Supports NULL value for World if the permission system registered supports global permissions.
536     * But May return odd values if the servers registered permission system does not have a global permission store.
537     * 
538     * @param world World Object
539     * @param player to check
540     * @param group Group name
541     * @return Success or Failure
542     */
543    public boolean playerInGroup(String world, OfflinePlayer player, String group) {
544        if (world == null) {
545            return playerInGroup((String) null, player.getName(), group);
546        }
547        return playerInGroup(world, player.getName(), group);
548    }
549
550    /**
551     * Check if player is member of a group.
552     * This method will ONLY check groups for which the player is in that are defined for the current world.
553     * This may result in odd return behaviour depending on what permission system has been registered.
554     * 
555     * @param player Player Object
556     * @param group Group name
557     * @return Success or Failure
558     */
559    public boolean playerInGroup(Player player, String group) {
560        return playerInGroup(player.getWorld().getName(), player, group);
561    }
562
563    /**
564     * @deprecated As of VaultAPI 1.4 use {@link #playerAddGroup(String, OfflinePlayer, String)} instead.
565     */
566    @Deprecated
567    abstract public boolean playerAddGroup(String world, String player, String group);
568
569    /**
570     * @deprecated As of VaultAPI 1.4 use {@link #playerAddGroup(String, OfflinePlayer, String)} instead.
571     */
572    @Deprecated
573    public boolean playerAddGroup(World world, String player, String group) {
574        if (world == null) {
575            return playerAddGroup((String) null, player, group);
576        }
577        return playerAddGroup(world.getName(), player, group);
578    }
579
580    /**
581     * Add player to a group.
582     * Supports NULL value for World if the permission system registered supports global permissions.
583     * But May return odd values if the servers registered permission system does not have a global permission store.
584     * 
585     * @param world String world name
586     * @param player to add
587     * @param group Group name
588     * @return Success or Failure
589     */
590    public boolean playerAddGroup(String world, OfflinePlayer player, String group) {
591        if (world == null) {
592            return playerAddGroup((String) null, player.getName(), group);
593        }
594        return playerAddGroup(world, player.getName(), group);
595    }
596    
597    /**
598     * Add player to a group.
599     * This will add a player to the group on the current World.  This may return odd results if the permission system
600     * being used on the server does not support world-specific groups, or if the group being added to is a global group.
601     * 
602     * @param player Player Object
603     * @param group Group name
604     * @return Success or Failure
605     */
606    public boolean playerAddGroup(Player player, String group) {
607        return playerAddGroup(player.getWorld().getName(), player, group);
608    }
609
610    /**
611     * @deprecated As of VaultAPI 1.4 use {@link #playerRemoveGroup(String, OfflinePlayer, String)} instead.
612     */
613    @Deprecated
614    abstract public boolean playerRemoveGroup(String world, String player, String group);
615
616    /**
617     * @deprecated As of VaultAPI 1.4 use {@link #playerRemoveGroup(String, OfflinePlayer, String)} instead.
618     */
619    @Deprecated
620    public boolean playerRemoveGroup(World world, String player, String group) {
621        if (world == null) {
622            return playerRemoveGroup((String) null, player, group);
623        }
624        return playerRemoveGroup(world.getName(), player, group);
625    }
626    
627    /**
628     * Remove player from a group.
629     * Supports NULL value for World if the permission system registered supports global permissions.
630     * But May return odd values if the servers registered permission system does not have a global permission store.
631     * 
632     * @param world World Object
633     * @param player to remove
634     * @param group Group name
635     * @return Success or Failure
636     */
637    public boolean playerRemoveGroup(String world, OfflinePlayer player, String group) {
638        if (world == null) {
639            return playerRemoveGroup((String) null, player.getName(), group);
640        }
641        return playerRemoveGroup(world, player.getName(), group);
642    }
643
644    /**
645     * Remove player from a group.
646     * This will add a player to the group on the current World.  This may return odd results if the permission system
647     * being used on the server does not support world-specific groups, or if the group being added to is a global group.
648     * 
649     * @param player Player Object
650     * @param group Group name
651     * @return Success or Failure
652     */
653    public boolean playerRemoveGroup(Player player, String group) {
654        return playerRemoveGroup(player.getWorld().getName(), player, group);
655    }
656
657    /**
658     * @deprecated As of VaultAPI 1.4 use {@link #getPlayerGroups(String, OfflinePlayer)} instead.
659     */
660    @Deprecated
661    abstract public String[] getPlayerGroups(String world, String player);
662
663    /**
664     * @deprecated As of VaultAPI 1.4 use {@link #getPlayerGroups(String, OfflinePlayer)} instead.
665     */
666    @Deprecated
667    public String[] getPlayerGroups(World world, String player) {
668        if (world == null) {
669            return getPlayerGroups((String) null, player);
670        }
671        return getPlayerGroups(world.getName(), player);
672    }
673    
674    /**
675     * Gets the list of groups that this player has
676     * Supports NULL value for World if the permission system registered supports global permissions.
677     * But May return odd values if the servers registered permission system does not have a global permission store.
678     * 
679     * @param world String world name
680     * @param player OfflinePlayer
681     * @return Array of groups
682     */
683    public String[] getPlayerGroups(String world, OfflinePlayer player) {
684        return getPlayerGroups(world, player.getName());
685    }
686
687    /**
688     * Returns a list of world-specific groups that this player is currently in. May return unexpected results if
689     * you are looking for global groups, or if the registered permission system does not support world-specific groups.
690     * See {@link #getPlayerGroups(String, OfflinePlayer)} for better control of World-specific or global groups.
691     * 
692     * @param player Player Object
693     * @return Array of groups
694     */
695    public String[] getPlayerGroups(Player player) {
696        return getPlayerGroups(player.getWorld().getName(), player);
697    }
698
699    /**
700     * @deprecated As of VaultAPI 1.4 use {@link #getPrimaryGroup(String, OfflinePlayer)} instead.
701     */
702    @Deprecated
703    abstract public String getPrimaryGroup(String world, String player);
704
705    /**
706     * @deprecated As of VaultAPI 1.4 use {@link #getPrimaryGroup(String, OfflinePlayer)} instead.
707     */
708    @Deprecated
709    public String getPrimaryGroup(World world, String player) {
710        if (world == null) {
711            return getPrimaryGroup((String) null, player);
712        }
713        return getPrimaryGroup(world.getName(), player);
714    }
715    
716    /**
717     * Gets players primary group
718     * Supports NULL value for World if the permission system registered supports global permissions.
719     * But May return odd values if the servers registered permission system does not have a global permission store.
720     * 
721     * @param world String world name
722     * @param player to get from
723     * @return Players primary group
724     */
725    public String getPrimaryGroup(String world, OfflinePlayer player) {
726        return getPrimaryGroup(world, player.getName());
727    }
728
729    /**
730     * Get players primary group.
731     * Defaults to the players current world, so may return only world-specific groups.
732     * In most cases {@link #getPrimaryGroup(String, OfflinePlayer)} is preferable.
733     * 
734     * @param player Player Object
735     * @return Players primary group
736     */
737    public String getPrimaryGroup(Player player) {
738        return getPrimaryGroup(player.getWorld().getName(), player);
739    }
740    
741    /**
742     * Returns a list of all known groups
743     * @return an Array of String of all groups
744     */
745    abstract public String[] getGroups();
746    
747    /**
748     * Returns true if the given implementation supports groups.
749     * @return true if the implementation supports groups
750     */
751    abstract public boolean hasGroupSupport();
752}