| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| package org.apache.directory.ldap.client.api; |
| |
| |
| import java.io.IOException; |
| import java.util.List; |
| |
| import org.apache.directory.shared.asn1.util.OID; |
| import org.apache.directory.shared.ldap.cursor.SearchCursor; |
| import org.apache.directory.shared.ldap.entry.Entry; |
| import org.apache.directory.shared.ldap.entry.Modification; |
| import org.apache.directory.shared.ldap.entry.ModificationOperation; |
| import org.apache.directory.shared.ldap.entry.Value; |
| import org.apache.directory.shared.ldap.exception.LdapException; |
| import org.apache.directory.shared.ldap.filter.SearchScope; |
| import org.apache.directory.shared.ldap.message.AbandonRequest; |
| import org.apache.directory.shared.ldap.message.AddRequest; |
| import org.apache.directory.shared.ldap.message.AddResponse; |
| import org.apache.directory.shared.ldap.message.BindRequest; |
| import org.apache.directory.shared.ldap.message.BindResponse; |
| import org.apache.directory.shared.ldap.message.CompareRequest; |
| import org.apache.directory.shared.ldap.message.CompareResponse; |
| import org.apache.directory.shared.ldap.message.DeleteRequest; |
| import org.apache.directory.shared.ldap.message.DeleteResponse; |
| import org.apache.directory.shared.ldap.message.ExtendedRequest; |
| import org.apache.directory.shared.ldap.message.ExtendedResponse; |
| import org.apache.directory.shared.ldap.message.ModifyDnRequest; |
| import org.apache.directory.shared.ldap.message.ModifyDnResponse; |
| import org.apache.directory.shared.ldap.message.ModifyRequest; |
| import org.apache.directory.shared.ldap.message.ModifyResponse; |
| import org.apache.directory.shared.ldap.message.SearchRequest; |
| import org.apache.directory.shared.ldap.name.DN; |
| import org.apache.directory.shared.ldap.name.RDN; |
| import org.apache.directory.shared.ldap.schema.SchemaManager; |
| |
| |
| /** |
| * The root interface for all the LDAP connection implementations. |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| */ |
| public interface LdapConnection |
| { |
| /** |
| * Check if we are connected |
| * |
| * @return <code>true</code> if we are connected. |
| */ |
| boolean isConnected(); |
| |
| |
| /** |
| * Check if we are authenticated |
| * |
| * @return <code>true</code> if we are connected. |
| */ |
| boolean isAuthenticated(); |
| |
| |
| /** |
| * Connect to the remote LDAP server. |
| * |
| * @return <code>true</code> if the connection is established, false otherwise |
| * @throws LdapException if some error occurred |
| * @throws IOException if an I/O exception occurred |
| */ |
| boolean connect() throws LdapException, IOException; |
| |
| |
| /** |
| * Disconnect from the remote LDAP server |
| * |
| * @return <code>true</code> if the connection is closed, false otherwise |
| * @throws IOException if some I/O error occurs |
| */ |
| boolean close() throws IOException; |
| |
| |
| //------------------------ The LDAP operations ------------------------// |
| // Add operations // |
| //---------------------------------------------------------------------// |
| /** |
| * Add an entry to the server. This is a blocking add : the user has |
| * to wait for the response until the AddResponse is returned. |
| * |
| * @param entry The entry to add |
| * @return the add operation's response |
| * @throws LdapException if some error occurred |
| */ |
| AddResponse add( Entry entry ) throws LdapException; |
| |
| |
| /** |
| * Add an entry present in the AddRequest to the server. |
| * |
| * @param addRequest the request object containing an entry and controls(if any) |
| * @return the add operation's response |
| * @throws LdapException if some error occurred |
| */ |
| AddResponse add( AddRequest addRequest ) throws LdapException; |
| |
| |
| /** |
| * Abandons a request submitted to the server for performing a particular operation |
| * |
| * The abandonRequest is always non-blocking, because no response is expected |
| * |
| * @param messageId the ID of the request message sent to the server |
| */ |
| void abandon( int messageId ); |
| |
| |
| /** |
| * An abandon request essentially with the request message ID of the operation to be canceled |
| * and/or potentially some controls and timeout (the controls and timeout are not mandatory). |
| * |
| * The abandonRequest is always non-blocking, because no response is expected |
| * |
| * @param abandonRequest the abandon operation's request |
| */ |
| void abandon( AbandonRequest abandonRequest ); |
| |
| |
| /** |
| * Anonymous Bind on a server. |
| * |
| * @return The BindResponse LdapResponse |
| * @throws LdapException if some error occurred |
| * @throws IOException if an I/O exception occurred |
| */ |
| BindResponse bind() throws LdapException, IOException; |
| |
| |
| /** |
| * Simple Bind on a server. |
| * |
| * @param name The name we use to authenticate the user. It must be a |
| * valid DN |
| * @param credentials The password. It can't be null |
| * @return The BindResponse LdapResponse |
| * @throws LdapException if some error occurred |
| * @throws IOException if an I/O exception occurred |
| */ |
| BindResponse bind( String name, String credentials ) throws LdapException, IOException; |
| |
| |
| /** |
| * Simple Bind on a server. |
| * |
| * @param name The name we use to authenticate the user. It must be a |
| * valid DN |
| * @param credentials The password. It can't be null |
| * @return The BindResponse LdapResponse |
| * @throws LdapException if some error occurred |
| * @throws IOException if an I/O exception occurred |
| */ |
| BindResponse bind( DN name, String credentials ) throws LdapException, IOException; |
| |
| |
| /** |
| * Bind to the server using a BindRequest object. |
| * |
| * @param bindRequest The BindRequest POJO containing all the needed |
| * parameters |
| * @return A LdapResponse containing the result |
| * @throws LdapException if some error occurred |
| * @throws IOException if an I/O exception occurred |
| */ |
| BindResponse bind( BindRequest bindRequest ) throws LdapException, IOException; |
| |
| |
| /** |
| * Do a search, on the base object, using the given filter. The |
| * SearchRequest parameters default to : |
| * Scope : ONE |
| * DerefAlias : ALWAYS |
| * SizeLimit : none |
| * TimeLimit : none |
| * TypesOnly : false |
| * Attributes : all the user's attributes. |
| * This method is blocking. |
| * |
| * @param baseDn The base for the search. It must be a valid |
| * DN, and can't be emtpy |
| * @param filter The filter to use for this search. It can't be empty |
| * @param scope The search scope : OBJECT, ONELEVEL or SUBTREE |
| * @param attributes The attributes to use for this search |
| * @return A search cursor on the result. |
| * @throws LdapException if some error occurred |
| */ |
| SearchCursor search( DN baseDn, String filter, SearchScope scope, String... attributes ) |
| throws LdapException; |
| |
| |
| /** |
| * Do a search, on the base object, using the given filter. The |
| * SearchRequest parameters default to : |
| * Scope : ONE |
| * DerefAlias : ALWAYS |
| * SizeLimit : none |
| * TimeLimit : none |
| * TypesOnly : false |
| * Attributes : all the user's attributes. |
| * This method is blocking. |
| * |
| * @param baseDn The base for the search. It must be a valid |
| * DN, and can't be emtpy |
| * @param filter The filter to use for this search. It can't be empty |
| * @param scope The search scope : OBJECT, ONELEVEL or SUBTREE |
| * @param attributes The attributes to use for this search |
| * @return A search cursor on the result. |
| * @throws LdapException if some error occurred |
| */ |
| SearchCursor search( String baseDn, String filter, SearchScope scope, String... attributes ) |
| throws LdapException; |
| |
| |
| /** |
| * Performs search in a synchronous mode. |
| * |
| * @param searchRequest The search configuration |
| * @return a search cursor on the result. |
| * @throws LdapException if some error occurred |
| */ |
| SearchCursor search( SearchRequest searchRequest ) throws LdapException; |
| |
| |
| //------------------------ The LDAP operations ------------------------// |
| // Unbind operations // |
| //---------------------------------------------------------------------// |
| /** |
| * UnBind from a server. This is a request which expect no response. |
| * @throws LdapException if some error occurred |
| */ |
| void unBind() throws LdapException; |
| |
| |
| /** |
| * Set the timeOut for the responses. We wont wait longer than this |
| * value. |
| * |
| * @param timeOut The timeout, in milliseconds |
| */ |
| void setTimeOut( long timeOut ); |
| |
| |
| /** |
| * Applies all the modifications to the entry specified by its DN. |
| * |
| * @param dn The entry's DN |
| * @param modifications The list of modifications to be applied |
| * @return the modify operation's response |
| * @throws LdapException in case of modify operation failure or timeout happens |
| */ |
| ModifyResponse modify( DN dn, Modification... modifications ) throws LdapException; |
| |
| |
| /** |
| * Applies all the modifications to the entry specified by its DN. |
| * |
| * @param dn The entry's DN |
| * @param modifications The list of modifications to be applied |
| * @return the modify operation's response |
| * @throws LdapException in case of modify operation failure or timeout happens |
| */ |
| ModifyResponse modify( String dn, Modification... modifications ) throws LdapException; |
| |
| |
| /** |
| * Modifies all the attributes present in the entry by applying the same operation. |
| * |
| * @param entry the entry with the attributes to be modified |
| * @param modOp the operation to be applied on all the attributes of the above entry |
| * @return the modify operation's response |
| * @throws LdapException in case of modify operation failure or timeout happens |
| */ |
| ModifyResponse modify( Entry entry, ModificationOperation modOp ) throws LdapException; |
| |
| |
| /** |
| * Performs an modify operation based on the modifications present in |
| * the ModifyRequest. |
| * |
| * @param modRequest the request for modify operation |
| * @return the modify operation's response |
| * @throws LdapException in case of modify operation failure or timeout happens |
| */ |
| ModifyResponse modify( ModifyRequest modRequest ) throws LdapException; |
| |
| |
| /** |
| * Renames the given entryDn with new RDN and deletes the old RDN. |
| * |
| * @param entryDn the target DN |
| * @param newRdn new RDN for the target DN |
| * @return modifyDn operation's response |
| * @throws LdapException if some error occurred |
| * @see #rename(String, String, boolean) |
| */ |
| ModifyDnResponse rename( String entryDn, String newRdn ) throws LdapException; |
| |
| |
| /** |
| * Renames the given entryDn with new RDN and deletes the old RDN. |
| * |
| * @param entryDn the target DN |
| * @param newRdn new RDN for the target DN |
| * @return modifyDn operation's response |
| * @throws LdapException if some error occurred |
| * @see #rename(DN, RDN, boolean) |
| */ |
| ModifyDnResponse rename( DN entryDn, RDN newRdn ) throws LdapException; |
| |
| |
| /** |
| * Renames the given entryDn with new RDN and deletes the old RDN if |
| * deleteOldRdn is set to true. |
| * |
| * @param entryDn the target DN |
| * @param newRdn new RDN for the target DN |
| * @param deleteOldRdn flag to indicate whether to delete the old RDN |
| * @return modifyDn operation's response |
| * @throws LdapException if some error occurred |
| * @see #rename(DN, RDN, boolean) |
| */ |
| ModifyDnResponse rename( String entryDn, String newRdn, boolean deleteOldRdn ) throws LdapException; |
| |
| |
| /** |
| * Renames the given entryDn with new RDN and deletes the old RDN if |
| * deleteOldRdn is set to true. |
| * |
| * @param entryDn the target DN |
| * @param newRdn new RDN for the target DN |
| * @param deleteOldRdn flag to indicate whether to delete the old RDN |
| * @return modifyDn operation's response |
| * @throws LdapException if some error occurred |
| */ |
| ModifyDnResponse rename( DN entryDn, RDN newRdn, boolean deleteOldRdn ) throws LdapException; |
| |
| |
| /** |
| * Moves the given entry DN under the new superior DN. |
| * |
| * @param entryDn the DN of the target entry |
| * @param newSuperiorDn DN of the new parent/superior |
| * @return modifyDn operation's response |
| * @throws LdapException if some error occurred |
| * @see #move(DN, DN) |
| */ |
| ModifyDnResponse move( String entryDn, String newSuperiorDn ) throws LdapException; |
| |
| |
| /** |
| * Moves the given entry DN under the new superior DN. |
| * |
| * @param entryDn the DN of the target entry |
| * @param newSuperiorDn DN of the new parent/superior |
| * @return modifyDn operation's response |
| * @throws LdapException if some error occurred |
| */ |
| ModifyDnResponse move( DN entryDn, DN newSuperiorDn ) throws LdapException; |
| |
| |
| /** |
| * Moves and renames the given entryDn. The old RDN will be deleted. |
| * |
| * @param entryDn The original entry DN |
| * @param newDn The new Entry DN |
| * @return modifyDn operations response |
| * @throws LdapException if some error occurred |
| * @see #moveAndRename(DN, DN, boolean) |
| */ |
| ModifyDnResponse moveAndRename( DN entryDn, DN newDn ) throws LdapException; |
| |
| |
| /** |
| * Moves and renames the given entryDn.The old RDN will be deleted |
| * |
| * @param entryDn The original entry DN |
| * @param newDn The new Entry DN |
| * @return modifyDn operations response |
| * @throws LdapException if some error occurred |
| * @see #moveAndRename(DN, DN, boolean) |
| */ |
| ModifyDnResponse moveAndRename( String entryDn, String newDn ) throws LdapException; |
| |
| |
| /** |
| * Moves and renames the given entryDn. The old RDN will be deleted if requested. |
| * |
| * @param entryDn The original entry DN |
| * @param newDn The new Entry DN |
| * @param deleteOldRdn Tells if the old RDN must be removed |
| * @return modifyDn operations response |
| * @throws LdapException if some error occurred |
| */ |
| ModifyDnResponse moveAndRename( DN entryDn, DN newDn, boolean deleteOldRdn ) throws LdapException; |
| |
| |
| /** |
| * Moves and renames the given entryDn. The old RDN will be deleted if requested. |
| * |
| * @param entryDn The original entry DN |
| * @param newDn The new Entry DN |
| * @param deleteOldRdn Tells if the old RDN must be removed |
| * @return modifyDn operation's response |
| * @throws LdapException if some error occurred |
| */ |
| ModifyDnResponse moveAndRename( String entryDn, String newDn, boolean deleteOldRdn ) |
| throws LdapException; |
| |
| |
| /** |
| * Performs the modifyDn operation based on the given ModifyDnRequest. |
| * |
| * @param modDnRequest the request |
| * @return modifyDn operation's response |
| * @throws LdapException if some error occurred |
| */ |
| ModifyDnResponse modifyDn( ModifyDnRequest modDnRequest ) throws LdapException; |
| |
| |
| /** |
| * Deletes the entry with the given DN. |
| * |
| * @param dn the target entry's DN as a String |
| * @return the delete operation's response |
| * @throws LdapException If the DN is not valid or if the deletion failed |
| */ |
| DeleteResponse delete( String dn ) throws LdapException; |
| |
| |
| /** |
| * Deletes the entry with the given DN. |
| * |
| * @param dn the target entry's DN |
| * @return the delete operation's response |
| * @throws LdapException If the DN is not valid or if the deletion failed |
| */ |
| DeleteResponse delete( DN dn ) throws LdapException; |
| |
| |
| /** |
| * Performs a delete operation based on the delete request object. |
| * |
| * @param deleteRequest the delete operation's request |
| * @return delete operation's response, null if a non-null listener value is provided |
| * @throws LdapException If the DN is not valid or if the deletion failed |
| */ |
| DeleteResponse delete( DeleteRequest deleteRequest ) throws LdapException; |
| |
| |
| /** |
| * Compares whether a given attribute's value matches that of the |
| * existing value of the attribute present in the entry with the given DN. |
| * |
| * @param dn the target entry's String DN |
| * @param attributeName the attribute's name |
| * @param value a String value with which the target entry's attribute value to be compared with |
| * @return compare operation's response |
| * @throws LdapException if some error occurred |
| */ |
| CompareResponse compare( String dn, String attributeName, String value ) throws LdapException; |
| |
| |
| /** |
| * Compares whether a given attribute's value matches that of the |
| * existing value of the attribute present in the entry with the given DN. |
| * |
| * @param dn the target entry's String DN |
| * @param attributeName the attribute's name |
| * @param value a byte[] value with which the target entry's attribute value to be compared with |
| * @return compare operation's response |
| * @throws LdapException if some error occurred |
| */ |
| CompareResponse compare( String dn, String attributeName, byte[] value ) throws LdapException; |
| |
| |
| /** |
| * Compares whether a given attribute's value matches that of the |
| * existing value of the attribute present in the entry with the given DN. |
| * |
| * @param dn the target entry's String DN |
| * @param attributeName the attribute's name |
| * @param value a Value<?> value with which the target entry's attribute value to be compared with |
| * @return compare operation's response |
| * @throws LdapException if some error occurred |
| */ |
| CompareResponse compare( String dn, String attributeName, Value<?> value ) throws LdapException; |
| |
| |
| /** |
| * Compares whether a given attribute's value matches that of the |
| * existing value of the attribute present in the entry with the given DN. |
| * |
| * @param dn the target entry's DN |
| * @param attributeName the attribute's name |
| * @param value a String value with which the target entry's attribute value to be compared with |
| * @return compare operation's response |
| * @throws LdapException if some error occurred |
| */ |
| CompareResponse compare( DN dn, String attributeName, String value ) throws LdapException; |
| |
| |
| /** |
| * Compares whether a given attribute's value matches that of the |
| * existing value of the attribute present in the entry with the given DN. |
| * |
| * @param dn the target entry's DN |
| * @param attributeName the attribute's name |
| * @param value a byte[] value with which the target entry's attribute value to be compared with |
| * @return compare operation's response |
| * @throws LdapException if some error occurred |
| */ |
| CompareResponse compare( DN dn, String attributeName, byte[] value ) throws LdapException; |
| |
| |
| /** |
| * Compares whether a given attribute's value matches that of the |
| * existing value of the attribute present in the entry with the given DN. |
| * |
| * @param dn the target entry's DN |
| * @param attributeName the attribute's name |
| * @param value a Value<?> value with which the target entry's attribute value to be compared with |
| * @return compare operation's response |
| * @throws LdapException if some error occurred |
| */ |
| CompareResponse compare( DN dn, String attributeName, Value<?> value ) throws LdapException; |
| |
| |
| /** |
| * Compares an entry's attribute's value with that of the given value. |
| * |
| * @param compareRequest the CompareRequest which contains the target DN, attribute name and value |
| * @return compare operation's response |
| * @throws LdapException if some error occurred |
| */ |
| CompareResponse compare( CompareRequest compareRequest ) throws LdapException; |
| |
| |
| /** |
| * Sends a extended operation request to the server with the given OID and no value. |
| * |
| * @param oid the object identifier of the extended operation |
| * @return extended operation's response |
| * @throws LdapException if some error occurred |
| * @see #extended(org.apache.directory.shared.asn1.util.OID, byte[]) |
| */ |
| ExtendedResponse extended( String oid ) throws LdapException; |
| |
| |
| /** |
| * Sends a extended operation request to the server with the given OID and value. |
| * |
| * @param oid the object identifier of the extended operation |
| * @param value value to be used by the extended operation, can be a null value |
| * @return extended operation's response |
| * @throws LdapException if some error occurred |
| * @see #extended(org.apache.directory.shared.asn1.util.OID, byte[]) |
| */ |
| ExtendedResponse extended( String oid, byte[] value ) throws LdapException; |
| |
| |
| /** |
| * Sends a extended operation request to the server with the given OID and no value. |
| * |
| * @param oid the object identifier of the extended operation |
| * @return extended operation's response |
| * @throws LdapException if some error occurred |
| * @see #extended(org.apache.directory.shared.asn1.util.OID, byte[]) |
| */ |
| ExtendedResponse extended( OID oid ) throws LdapException; |
| |
| |
| /** |
| * Sends a extended operation request to the server with the given OID and value. |
| * |
| * @param oid the object identifier of the extended operation |
| * @param value value to be used by the extended operation, can be a null value |
| * @return extended operation's response |
| * @throws LdapException if some error occurred |
| */ |
| ExtendedResponse extended( OID oid, byte[] value ) throws LdapException; |
| |
| |
| /** |
| * Performs an extended operation based on the Extended request object. |
| * |
| * @param extendedRequest the extended operation's request |
| * @return Extended operation's response |
| * @throws LdapException If the DN is not valid or if the extended operation failed |
| */ |
| ExtendedResponse extended( ExtendedRequest extendedRequest ) throws LdapException; |
| |
| |
| /** |
| * Tells if an Entry exists in the server. |
| * |
| * @param dn The DN for the entry we want to check the existence |
| * @return <code>true</code> if the entry exists, <code>false</code> otherwise. |
| * Note that if the entry exists but if the user does not have the permission to |
| * read it, <code>false</code> will also be returned |
| * @throws LdapException if some error occurred |
| */ |
| boolean exists( String dn ) throws LdapException; |
| |
| |
| /** |
| * Tells if an Entry exists in the server. |
| * |
| * @param dn The DN for the entry we want to check the existence |
| * @return <code>true</code> if the entry exists, <code>false</code> otherwise. |
| * Note that if the entry exists but if the user does not have the permission to |
| * read it, <code>false</code> will also be returned |
| * @throws LdapException if some error occurred |
| */ |
| boolean exists( DN dn ) throws LdapException; |
| |
| |
| /** |
| * Searches for an entry having the given DN. |
| * |
| * @param dn the DN of the entry to be fetched |
| * @return the Entry with the given DN or null if no entry exists with that DN |
| * @throws LdapException in case of any problems while searching for the DN or if the returned response contains a referral |
| * @see #lookup(DN, String...) |
| */ |
| Entry lookup( DN dn ) throws LdapException; |
| |
| |
| /** |
| * Searches for an entry having the given DN. |
| * |
| * @param dn the DN of the entry to be fetched |
| * @return the Entry with the given DN or null if no entry exists with that DN |
| * @throws LdapException in case of any problems while searching for the DN or if the returned response contains a referral |
| * @see #lookup(String, String...) |
| */ |
| Entry lookup( String dn ) throws LdapException; |
| |
| |
| /** |
| * Searches for an entry having the given DN. |
| * |
| * @param dn the DN of the entry to be fetched |
| * @param attributes the attributes to be returned along with entry |
| * @return the Entry with the given DN or null if no entry exists with that DN |
| * @throws LdapException in case of any problems while searching for the DN or if the returned response contains a referral |
| */ |
| Entry lookup( DN dn, String... attributes ) throws LdapException; |
| |
| |
| /** |
| * Searches for an entry having the given DN. |
| * |
| * @param dn the DN of the entry to be fetched |
| * @param attributes the attributes to be returned along with entry |
| * @return the Entry with the given DN or null if no entry exists with that DN |
| * @throws LdapException in case of any problems while searching for the DN or if the returned response contains a referral |
| * @see #lookup(DN, String...) |
| */ |
| Entry lookup( String dn, String... attributes ) throws LdapException; |
| |
| |
| /** |
| * Checks if a control with the given OID is supported. |
| * |
| * @param controlOID the OID of the control |
| * @return true if the control is supported, false otherwise |
| * @throws LdapException if some error occurred |
| */ |
| boolean isControlSupported( String controlOID ) throws LdapException; |
| |
| |
| /** |
| * Get the Controls supported by server. |
| * |
| * @return a list of control OIDs supported by server |
| * @throws LdapException if some error occurred |
| */ |
| List<String> getSupportedControls() throws LdapException; |
| |
| |
| /** |
| * Loads all the default schemas that are bundled with the API.<br><br> |
| * <b>Note:</b> This method enables <b>all</b> schemas prior to loading |
| * @throws LdapException in case of problems while loading the schema |
| */ |
| void loadSchema() throws LdapException; |
| |
| |
| /** |
| * @return The SchemaManager associated with this LdapConection if any |
| */ |
| SchemaManager getSchemaManager(); |
| |
| |
| /** |
| * Checks if there is a ResponseFuture associated with the given message ID. |
| * |
| * @param messageId ID of the request |
| * @return true if there is a non-null future exists, false otherwise |
| */ |
| boolean doesFutureExistFor( int messageId ); |
| |
| } |