All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hsqldb.rights.Grantee Maven / Gradle / Ivy

There is a newer version: 2.7.2
Show newest version
/* Copyright (c) 2001-2019, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


package org.hsqldb.rights;

import org.hsqldb.Expression;
import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.NumberSequence;
import org.hsqldb.Routine;
import org.hsqldb.SchemaObject;
import org.hsqldb.Session;
import org.hsqldb.Table;
import org.hsqldb.Tokens;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.HashMap;
import org.hsqldb.lib.HsqlArrayList;
import org.hsqldb.lib.Iterator;
import org.hsqldb.lib.MultiValueHashMap;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.types.Type;

/**
 * A Grantee Object holds the name, access and administrative rights for a
 * particular grantee.

* It supplies the methods used to grant, revoke, test * and check a grantee's access rights to other database objects. * It also holds a reference to the common PUBLIC User Object, * which represent the special user referred to in * GRANT ... TO PUBLIC statements.

* The check(), isAccessible() and getGrantedClassNames() methods check the * rights granted to the PUBLIC User Object, in addition to individually * granted rights, in order to decide which rights exist for the user. * * Method names ending in Direct indicate methods which do not recurse * to look through Roles which "this" object is a member of. * * We use the word "Admin" (e.g., in private variable "admin" and method * "isAdmin()) to mean this Grantee has admin priv by any means. * We use the word "adminDirect" (e.g., in private variable "adminDirect" * and method "isAdminDirect()) to mean this Grantee has admin priv * directly. * * @author Campbell Burnet (campbell-burnet@users dot sourceforge.net) * @author Fred Toussi (fredt@users dot sourceforge.net) * @author Blaine Simpson (blaine dot simpson at admc dot com) * * @version 2.3.4 * @since 1.8.0 */ public class Grantee implements SchemaObject { boolean isRole; /** * true if this grantee has database administrator priv directly * (ie., not by membership in any role) */ private boolean isAdminDirect = false; /** true if this grantee has database administrator priv by any means. */ private boolean isAdmin = false; /** true if this grantee is PUBLIC. */ boolean isPublic = false; /** true if this grantee is _SYSTEM. */ boolean isSystem = false; /** Grantee name. */ protected HsqlName granteeName; /** map with database object identifier keys and access privileges values */ private MultiValueHashMap directRightsMap; /** contains rights granted direct, or via roles, except those of PUBLIC */ HashMap fullRightsMap; /** These are the DIRECT roles. Each of these may contain nested roles */ OrderedHashSet roles; /** map with database object identifier keys and access privileges values */ private MultiValueHashMap grantedRightsMap; /** Needed only to give access to the roles for this database */ protected GranteeManager granteeManager; /** */ protected Right ownerRights; /** * Constructor. */ Grantee(HsqlName name, GranteeManager man) { fullRightsMap = new HashMap(); directRightsMap = new MultiValueHashMap(); grantedRightsMap = new MultiValueHashMap(); granteeName = name; granteeManager = man; roles = new OrderedHashSet(); ownerRights = new Right(); ownerRights.isFull = true; ownerRights.grantor = GranteeManager.systemAuthorisation; ownerRights.grantee = this; } public int getType() { return SchemaObject.GRANTEE; } public HsqlName getName() { return granteeName; } public HsqlName getSchemaName() { return null; } public HsqlName getCatalogName() { return null; } public Grantee getOwner() { return null; } public OrderedHashSet getReferences() { return new OrderedHashSet(); } public OrderedHashSet getComponents() { return null; } public void compile(Session session, SchemaObject parentObject) {} public String getSQL() { StringBuilder sb = new StringBuilder(); sb.append(Tokens.T_CREATE).append(' ').append(Tokens.T_ROLE); sb.append(' ').append(granteeName.statementName); return sb.toString(); } public long getChangeTimestamp() { return 0; } public boolean isRole() { return isRole; } public boolean isSystem() { return isSystem; } /** * Gets direct roles, not roles nested within them. */ public OrderedHashSet getDirectRoles() { return roles; } /** * Gets direct and indirect roles. */ public OrderedHashSet getAllRoles() { OrderedHashSet set = getGranteeAndAllRoles(); // Since we added "Grantee" in addition to Roles, need to remove self. set.remove(this); return set; } public OrderedHashSet getGranteeAndAllRoles() { OrderedHashSet set = new OrderedHashSet(); addGranteeAndRoles(set); return set; } public OrderedHashSet getGranteeAndAllRolesWithPublic() { OrderedHashSet set = new OrderedHashSet(); addGranteeAndRoles(set); set.add(granteeManager.publicRole); return set; } public boolean isAccessible(HsqlName name, int privilegeType) { if (isFullyAccessibleByRole(name)) { return true; } Right right = (Right) fullRightsMap.get(name); if (right == null) { return false; } return right.canAccess(privilegeType); } /** * returns true if grantee has any privilege (to any column) of the object */ public boolean isAccessible(SchemaObject object) { return isAccessible(object.getName()); } public boolean isAccessible(HsqlName name) { if (isFullyAccessibleByRole(name)) { return true; } Right right = (Right) fullRightsMap.get(name); if (right != null && !right.isEmpty()) { return true; } if (!isPublic) { return granteeManager.publicRole.isAccessible(name); } return false; } /** * Adds to given Set this.sName plus all roles and nested roles. * * @return Given role with new elements added. */ private OrderedHashSet addGranteeAndRoles(OrderedHashSet set) { Grantee candidateRole; set.add(this); for (int i = 0; i < roles.size(); i++) { candidateRole = (Grantee) roles.get(i); if (!set.contains(candidateRole)) { candidateRole.addGranteeAndRoles(set); } } return set; } private boolean hasRoleDirect(Grantee role) { return roles.contains(role); } public boolean hasRole(Grantee role) { return getAllRoles().contains(role); } /** * Grants the specified rights on the specified database object.

* * Keys stored in rightsMap for database tables are their HsqlName * attribute. This allows rights to persist when a table is renamed.

*/ void grant(HsqlName name, Right right, Grantee grantor, boolean withGrant) { final Right grantableRights = grantor.getAllGrantableRights(name); Right existingRight = null; if (right == Right.fullRights) { if (grantableRights.isEmpty()) { return; // has no rights } right = grantableRights; } else { if (!grantableRights.contains(right)) { throw Error.error(ErrorCode.X_0L000); } } Iterator it = directRightsMap.get(name); while (it.hasNext()) { Right existing = (Right) it.next(); if (existing.grantor == grantor) { existingRight = existing; existingRight.add(right); break; } } if (existingRight == null) { existingRight = right.duplicate(); existingRight.grantor = grantor; existingRight.grantee = this; directRightsMap.put(name, existingRight); } if (withGrant) { if (existingRight.grantableRights == null) { existingRight.grantableRights = right.duplicate(); } else { existingRight.grantableRights.add(right); } } if (!grantor.isSystem()) { // based on assumption that there is no need to access grantor.grantedRightsMap.put(name, existingRight); } updateAllRights(); } /** * Revokes the specified rights on the specified database object.

* * If, after removing the specified rights, no rights remain on the * database object, then the key/value pair for that object is removed * from the rights map */ void revoke(SchemaObject object, Right right, Grantee grantor, boolean grantOption) { HsqlName name = object.getName(); if (object instanceof Routine) { name = ((Routine) object).getSpecificName(); } Iterator it = directRightsMap.get(name); Right existing = null; while (it.hasNext()) { existing = (Right) it.next(); if (existing.grantor == grantor) { break; } } if (existing == null) { return; } if (existing.grantableRights != null) { existing.grantableRights.remove(object, right); } if (grantOption) { return; } if (right.isFull) { directRightsMap.remove(name, existing); grantor.grantedRightsMap.remove(name, existing); updateAllRights(); return; } existing.remove(object, right); if (existing.isEmpty()) { directRightsMap.remove(name, existing); grantor.grantedRightsMap.remove(name, existing); } updateAllRights(); } /** * Revokes all rights on the specified database object.

* * This method removes any existing mapping from the rights map */ void revokeDbObject(HsqlName name) { directRightsMap.remove(name); grantedRightsMap.remove(name); fullRightsMap.remove(name); } /** * Update own table column set rights to include a newly created column. * * This is done by checking that a mapping exists in the rights map * from the dbobject argument. Otherwise, it throws. */ public Right checkSelect(SchemaObject object, boolean[] checkList) { if (object instanceof Table) { Table table = (Table) object; if (isFullyAccessibleByRole(table.getName())) { return Right.fullRights; } Right right = (Right) fullRightsMap.get(table.getName()); if (right != null && right.canSelect(table, checkList)) { return right; } } throw Error.error(ErrorCode.X_42501, object.getName().name); } public void checkInsert(SchemaObject object, boolean[] checkList) { if (object instanceof Table) { Table table = (Table) object; if (isFullyAccessibleByRole(table.getName())) { return; } Right right = (Right) fullRightsMap.get(table.getName()); if (right != null && right.canInsert(table, checkList)) { return; } } throw Error.error(ErrorCode.X_42501, object.getName().name); } public void checkUpdate(SchemaObject object, boolean[] checkList) { if (object instanceof Table) { Table table = (Table) object; if (isFullyAccessibleByRole(table.getName())) { return; } Right right = (Right) fullRightsMap.get(table.getName()); if (right != null && right.canUpdate(table, checkList)) { return; } } throw Error.error(ErrorCode.X_42501, object.getName().name); } public void checkReferences(SchemaObject object, boolean[] checkList) { if (object instanceof Table) { Table table = (Table) object; if (isFullyAccessibleByRole(table.getName())) { return; } Right right = (Right) fullRightsMap.get(table.getName()); if (right != null && right.canReference(table, checkList)) { return; } } throw Error.error(ErrorCode.X_42501, object.getName().name); } public void checkTrigger(SchemaObject object, boolean[] checkList) { if (object instanceof Table) { Table table = (Table) object; if (isFullyAccessibleByRole(table.getName())) { return; } Right right = (Right) fullRightsMap.get(table.getName()); if (right != null && right.canReference(table, checkList)) { return; } } throw Error.error(ErrorCode.X_42501, object.getName().name); } public void checkDelete(SchemaObject object) { if (object instanceof Table) { Table table = (Table) object; if (isFullyAccessibleByRole(table.getName())) { return; } Right right = (Right) fullRightsMap.get(table.getName()); if (right != null && right.canDelete()) { return; } } throw Error.error(ErrorCode.X_42501, object.getName().name); } public void checkAccess(SchemaObject object) { if (isFullyAccessibleByRole(object.getName())) { return; } HsqlName name = object.getName(); if (object instanceof Routine) { name = ((Routine) object).getSpecificName(); } Right right = (Right) fullRightsMap.get(name); if (right != null && !right.isEmpty()) { return; } throw Error.error(ErrorCode.X_42501, object.getName().name); } /** * Checks if this object can modify schema objects or grant access rights * to them. */ public void checkSchemaUpdateOrGrantRights(String schemaName) { if (!hasSchemaUpdateOrGrantRights(schemaName)) { throw Error.error(ErrorCode.X_42501, schemaName); } } /** * Checks if this object can modify schema objects or grant access rights * to them. */ public boolean hasSchemaUpdateOrGrantRights(String schemaName) { // If a DBA if (isAdmin()) { return true; } Grantee schemaOwner = granteeManager.database.schemaManager.toSchemaOwner(schemaName); // If owner of Schema if (schemaOwner == this) { return true; } // If a member of Schema authorization role if (hasRole(schemaOwner)) { return true; } return false; } public boolean isGrantable(SchemaObject object, Right right) { if (isFullyAccessibleByRole(object.getName())) { return true; } Right grantableRights = getAllGrantableRights(object.getName()); return grantableRights.contains(right); } public boolean isGrantable(Grantee role) { return isAdmin; } public boolean isFullyAccessibleByRole(HsqlName name) { Grantee owner; if (isAdmin) { return true; } if (name.type == SchemaObject.SCHEMA) { owner = name.owner; } else if (name.schema == null) { return false; } else { owner = name.schema.owner; } if (owner == this) { return true; } if (hasRole(owner)) { return true; } return false; } /** * Checks whether this Grantee has administrative privs either directly * or indirectly. Otherwise it throws. */ public void checkAdmin() { if (!isAdmin()) { throw Error.error(ErrorCode.X_42507); } } /** * Returns true if this Grantee has administrative privs either directly * or indirectly. */ public boolean isAdmin() { return isAdmin; } /** * Returns true if this Grantee can create schemas with own authorization. */ public boolean isSchemaCreator() { return isAdmin || hasRole(granteeManager.schemaRole); } /** * Returns true if this Grantee can change to a different user. */ public boolean canChangeAuthorisation() { return isAdmin || hasRole(granteeManager.changeAuthRole); } /** * Returns true if this grantee object is for the PUBLIC role. */ public boolean isPublic() { return isPublic; } /** * Iteration of all visible grantees, including self.

* * For grantees with admin, this is all grantees. * For regular grantees, this is self plus all roles granted directly * or indirectly */ public OrderedHashSet visibleGrantees() { OrderedHashSet grantees = new OrderedHashSet(); GranteeManager gm = granteeManager; if (isAdmin()) { grantees.addAll(gm.getGrantees()); } else { grantees.add(this); Iterator it = getAllRoles().iterator(); while (it.hasNext()) { grantees.add(it.next()); } } return grantees; } public boolean hasNonSelectTableRight(SchemaObject table) { if (isFullyAccessibleByRole(table.getName())) { return true; } Right right = (Right) fullRightsMap.get(table.getName()); if (right == null) { return false; } return right.canAccesssNonSelect(); } public boolean hasColumnRights(SchemaObject table, int[] columnMap) { if (isFullyAccessibleByRole(table.getName())) { return true; } Right right = (Right) fullRightsMap.get(table.getName()); if (right == null) { return false; } return right.canAccess((Table) table, columnMap); } /** * Violates naming convention (for backward compatibility). * Should be "setAdminDirect(boolean"). */ void setAdminDirect() { isAdmin = isAdminDirect = true; } /** * Recursive method used with ROLE Grantee objects to set the fullRightsMap * and admin flag for all the roles. * * If a new ROLE is granted to a ROLE Grantee object, the ROLE should first * be added to the Set of ROLE Grantee objects (roles) for the grantee. * The grantee will be the parameter. * * If the direct permissions granted to an existing ROLE Grantee is * modified no extra initial action is necessary. * The existing Grantee will be the parameter. * * If an existing ROLE is REVOKEed from a ROLE, it should first be removed * from the set of ROLE Grantee objects in the containing ROLE. * The containing ROLE will be the parameter. * * If an existing ROLE is DROPped, all its privileges should be cleared * first. The ROLE will be the parameter. After calling this method on * all other roles, the DROPped role should be removed from all grantees. * * After the initial modification, this method should be called iteratively * on all the ROLE Grantee objects contained in RoleManager. * * The updateAllRights() method is then called iteratively on all the * USER Grantee objects contained in UserManager. * @param role a modified, revoked or dropped role. * @return true if this Grantee has possibly changed as a result */ boolean updateNestedRoles(Grantee role) { boolean hasNested = false; if (role != this) { for (int i = 0; i < roles.size(); i++) { Grantee currentRole = (Grantee) roles.get(i); hasNested |= currentRole.updateNestedRoles(role); } } if (hasNested) { updateAllRights(); } return hasNested || role == this; } /** * Method used with all Grantee objects to set the full set of rights * according to those inherited form ROLE Grantee objects and those * granted to the object itself. */ /** * @todo -- see if this is correct and the currentRole.fullRightsMap * is always updated prior to being added to this.fullRightsMap */ void updateAllRights() { fullRightsMap.clear(); isAdmin = isAdminDirect; for (int i = 0; i < roles.size(); i++) { Grantee currentRole = (Grantee) roles.get(i); addToFullRights(currentRole.fullRightsMap); isAdmin |= currentRole.isAdmin(); } addToFullRights(directRightsMap); if (!isRole && !isPublic && !isSystem) { addToFullRights(granteeManager.publicRole.fullRightsMap); } } /** * Full or partial rights are added to existing */ void addToFullRights(HashMap map) { Iterator it = map.keySet().iterator(); while (it.hasNext()) { Object key = it.next(); Right add = (Right) map.get(key); Right existing = (Right) fullRightsMap.get(key); if (existing == null) { existing = add.duplicate(); fullRightsMap.put(key, existing); } else { existing.add(add); } if (add.grantableRights == null) { continue; } if (existing.grantableRights == null) { existing.grantableRights = add.grantableRights.duplicate(); } else { existing.grantableRights.add(add.grantableRights); } } } /** * Full or partial rights are added to existing */ private void addToFullRights(MultiValueHashMap map) { Iterator it = map.keySet().iterator(); while (it.hasNext()) { Object key = it.next(); Iterator values = map.get(key); Right existing = (Right) fullRightsMap.get(key); while (values.hasNext()) { Right add = (Right) values.next(); if (existing == null) { existing = add.duplicate(); fullRightsMap.put(key, existing); } else { existing.add(add); } if (add.grantableRights == null) { continue; } if (existing.grantableRights == null) { existing.grantableRights = add.grantableRights.duplicate(); } else { existing.grantableRights.add(add.grantableRights); } } } } Right getAllGrantableRights(HsqlName name) { if (isAdmin) { return name.schema.owner.ownerRights; } if (name.schema.owner == this) { return ownerRights; } if (roles.contains(name.schema.owner)) { return name.schema.owner.ownerRights; } OrderedHashSet set = getAllRoles(); for (int i = 0; i < set.size(); i++) { Grantee role = (Grantee) set.get(i); if (name.schema.owner == role) { return role.ownerRights; } } Right right = (Right) fullRightsMap.get(name); return right == null || right.grantableRights == null ? Right.noRights : right .grantableRights; } /** * Retrieves the map object that represents the rights that have been * granted on database objects.

* * The map has keys and values with the following interpretation:

* *

    *
  • The keys are generally (but not limited to) objects having * an attribute or value equal to the name of an actual database * object. * *
  • Specifically, the keys act as database object identifiers. * *
  • The values are Right objects. *
*/ private MultiValueHashMap getRights() { // necessary to create the script return directRightsMap; } /** * Grant a role */ void grant(Grantee role) { roles.add(role); } /** * Revoke a direct role only */ void revoke(Grantee role) { if (!hasRoleDirect(role)) { throw Error.error(ErrorCode.X_0P503, role.getName().getNameString()); } roles.remove(role); } private String roleMapToString(OrderedHashSet roles) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < roles.size(); i++) { if (sb.length() > 0) { sb.append(','); } Grantee role = (Grantee) roles.get(i); sb.append(role.getName().getStatementName()); } return sb.toString(); } HsqlArrayList getRightsSQL() { HsqlArrayList list = new HsqlArrayList(); String roleString = roleMapToString(roles); if (roleString.length() != 0) { StringBuilder sb = new StringBuilder(128); sb.append(Tokens.T_GRANT).append(' ').append(roleString); sb.append(' ').append(Tokens.T_TO).append(' '); sb.append(getName().getStatementName()); list.add(sb.toString()); } MultiValueHashMap rightsMap = getRights(); Iterator dbObjects = rightsMap.keySet().iterator(); while (dbObjects.hasNext()) { Object nameObject = dbObjects.next(); Iterator rights = rightsMap.get(nameObject); while (rights.hasNext()) { Right right = (Right) rights.next(); StringBuilder sb = new StringBuilder(128); HsqlName hsqlname = (HsqlName) nameObject; switch (hsqlname.type) { case SchemaObject.TABLE : case SchemaObject.VIEW : Table table = granteeManager.database.schemaManager .findUserTable(hsqlname.name, hsqlname.schema.name); if (table != null) { sb.append(Tokens.T_GRANT).append(' '); sb.append(right.getTableRightsSQL(table)); sb.append(' ').append(Tokens.T_ON).append(' '); sb.append(Tokens.T_TABLE).append(' '); sb.append( hsqlname.getSchemaQualifiedStatementName()); Expression expr = right.getFilterExpression(); if (expr != null) { sb.append(' ').append(Tokens.T_FILTER); sb.append(Tokens.T_OPENBRACKET); sb.append(Tokens.T_WHERE).append(' '); sb.append(expr.getSQL()); sb.append(Tokens.T_CLOSEBRACKET); } } break; case SchemaObject.SEQUENCE : NumberSequence sequence = (NumberSequence) granteeManager.database .schemaManager .findSchemaObject(hsqlname.name, hsqlname.schema.name, SchemaObject.SEQUENCE); if (sequence != null) { sb.append(Tokens.T_GRANT).append(' '); sb.append(Tokens.T_USAGE); sb.append(' ').append(Tokens.T_ON).append(' '); sb.append(Tokens.T_SEQUENCE).append(' '); sb.append( hsqlname.getSchemaQualifiedStatementName()); } break; case SchemaObject.DOMAIN : Type domain = (Type) granteeManager.database.schemaManager .findSchemaObject(hsqlname.name, hsqlname.schema.name, SchemaObject.DOMAIN); if (domain != null) { sb.append(Tokens.T_GRANT).append(' '); sb.append(Tokens.T_USAGE); sb.append(' ').append(Tokens.T_ON).append(' '); sb.append(Tokens.T_DOMAIN).append(' '); sb.append( hsqlname.getSchemaQualifiedStatementName()); } break; case SchemaObject.TYPE : Type type = (Type) granteeManager.database.schemaManager .findSchemaObject(hsqlname.name, hsqlname.schema.name, SchemaObject.DOMAIN); if (type != null) { sb.append(Tokens.T_GRANT).append(' '); sb.append(Tokens.T_USAGE); sb.append(' ').append(Tokens.T_ON).append(' '); sb.append(Tokens.T_TYPE).append(' '); sb.append( hsqlname.getSchemaQualifiedStatementName()); } break; case SchemaObject.PROCEDURE : case SchemaObject.FUNCTION : case SchemaObject.SPECIFIC_ROUTINE : SchemaObject routine = granteeManager.database.schemaManager .findSchemaObject(hsqlname.name, hsqlname.schema.name, hsqlname.type); if (routine != null) { sb.append(Tokens.T_GRANT).append(' '); sb.append(Tokens.T_EXECUTE).append(' '); sb.append(Tokens.T_ON).append(' '); sb.append(Tokens.T_SPECIFIC).append(' '); if (routine.getType() == SchemaObject.PROCEDURE) { sb.append(Tokens.T_PROCEDURE); } else { sb.append(Tokens.T_FUNCTION); } sb.append(' '); sb.append( hsqlname.getSchemaQualifiedStatementName()); } break; default : } if (sb.length() == 0) { continue; } sb.append(' ').append(Tokens.T_TO).append(' '); sb.append(getName().getStatementName()); list.add(sb.toString()); } } return list; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy