/*!
  @header ECXMLControlDefaultRule
  @abstract Module of Encore
 
  @availability OS X, GNUstep
  @copyright 2004, 2005, 2006 Oliver Langer

  Author: Oliver Langer

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA

  <pre>
  -------------------------------------------------------------------------
  Modification history

  27.11.05 ola     initial version
  22.08.06 ola     license changed
  -------------------------------------------------------------------------
  </pre>
*/

#include <Encore/ECMemory.h>
#include <Encore/ECXMLControlDefaultRule.h>


@implementation ECXMLControlSetAttributeRule


- init {
  self = [super init];
  self->setterLookupUtil = [[ECXMLControlSetterLookupUtil alloc] init];
  
  return self;
}


- (void) dealloc {
  if( nil != self->setterLookupUtil ) {
    [self->setterLookupUtil release];
  }
  
  [super dealloc];
}

- (BOOL) mayActOn: (ECXMLControlContext *) aContext {
  return [[[aContext event] eventId] 
    isEqual: [ECXMLControlEvent eventId_addAttribute]];
}


- (BOOL) actOn: (ECXMLControlContext *) aContext {
  id userObject;
  BOOL ignoreSubsequentRules = NO;
  
  NSAssert( [[[aContext event] eventId] 
    isEqual: [ECXMLControlEvent eventId_addAttribute]],
    @"ECXMLControlSetAttributeRule::actOn: Expected an addAttribute event!" );
    
  // fetch user object at top position
  userObject = [[aContext state] userObjectAtTopPosition];
  
  if( nil == userObject ) {
    return NO;
  }
  
  EC_AUTORELEASEPOOL_BEGIN
    SEL methodToCall = [self->setterLookupUtil 
      lookupMethodForAttributeName: [[aContext event] attributeName]
      ofObject: userObject];
      
    if( NULL != methodToCall ) {
      [userObject performSelector: methodToCall 
            withObject: [[aContext event] attributeValue]];   
      ignoreSubsequentRules = YES;
    }
  EC_AUTORELEASEPOOL_END
  
  return ignoreSubsequentRules;
}
@end



// ------------------------------------- ECXMLControlLinkObjectRule

@implementation ECXMLControlLinkObjectRule

- init {
  self = [super init];
  self->setterLookupUtil = [[ECXMLControlSetterLookupUtil alloc] init];
  
  return self;
}


- (void) dealloc {
  if( nil != self->setterLookupUtil ) {
    [self->setterLookupUtil release];
  }
  
  [super dealloc];
}


- (BOOL) actOn: (ECXMLControlContext *) aContext {
  BOOL elementRemovedFromStack = NO;
  NSAssert( [[[aContext event] eventId] isEqual: 
    [ECXMLControlEvent eventId_elementEnded]],
    @"ECXMLControlLinkObjectRule::actOn: Expected an END ELEMENT event!" );
  
  EC_AUTORELEASEPOOL_BEGIN
  
  if( [[aContext state] countStackObjects] > 1 ) {
    /*
     * For the current top user object there exists a (potential) parent
     * object -> so remove it from the stack and add (link) it to the parent
     * object
     */
    id oldTopUserObject = [[[aContext state] popUserObject] autorelease];
    SEL methodToCall;
    
    elementRemovedFromStack = YES;
    
    /**
     * Look up for a related setter method of the current (new) top object.
     * Call it, if existent
     */
    methodToCall = [self->setterLookupUtil 
      lookupMethodForAttributeName: [[aContext event] elementName]
      ofObject: [[aContext state] userObjectAtTopPosition]];
    if( NULL != methodToCall ) {
      [[[aContext state] userObjectAtTopPosition] 
        performSelector: methodToCall 
        withObject: oldTopUserObject]; 
    }
  }
  
  EC_AUTORELEASEPOOL_END
  return elementRemovedFromStack;
}


- (BOOL) mayActOn: (ECXMLControlContext *) aContext {
  return [[[aContext event] eventId] isEqual: 
    [ECXMLControlEvent eventId_elementEnded]];
}

@end

// ------------------------------------- ECXMLControlSetterLookupUtil

@implementation ECXMLControlSetterLookupUtil

- init {
  self = [super init];
  self->namesForSetterMethods = [[NSMutableArray alloc] init];
  
  /*
   * The following format strings must contain "%@%@", where the
   * first substitution string will be replaced by the first letter of
   * the attribute name, which may be in lower or in upper case. The remaining
   * substition string will be replaced by the remaining characters of the 
   * attribute name
   */
  [self addMethodNameSchema: @"set%@%@:"];
  [self addMethodNameSchema: @"add%@%@:"];
  
  return self;
}


- (void) dealloc {
  if( nil != self->namesForSetterMethods ) {
    [self->namesForSetterMethods release];
  }
  
  [super dealloc];
}


- addMethodNameSchema: (NSString *) aMethodNameSchema {
  [self->namesForSetterMethods addObject: aMethodNameSchema];
  
  return self;
}


- (SEL) lookupMethodForAttributeName: (NSString *) anAttributeName 
  ofObject: (id) aTargetObject {
  SEL toReturn = NULL;
  
  EC_AUTORELEASEPOOL_BEGIN
    SEL methodToCall;
    int i;
    NSString *firstCharacter = [anAttributeName substringToIndex: 1];
    NSString *remainingChars = [anAttributeName substringFromIndex: 1];
    
    for( i = 0; i < [self->namesForSetterMethods count]; i++ ) {
       NSString *methodName;

       // try with the first character as uppercase character
       methodName = [[[NSString alloc] 
         initWithFormat: [self->namesForSetterMethods objectAtIndex: i],
         [firstCharacter uppercaseString],
         remainingChars] autorelease];
         
       methodToCall = NSSelectorFromString( methodName );
       if( NULL != methodToCall ) {
         if( [aTargetObject respondsToSelector: methodToCall] ) {
           // method found
           toReturn = methodToCall;
           break;
         }
       }
      
      // try with the first character as lowercase character
      methodName = [[[NSString alloc] 
        initWithFormat: [self->namesForSetterMethods objectAtIndex: i],
        [firstCharacter lowercaseString],
        remainingChars] autorelease];
         
      methodToCall = NSSelectorFromString( methodName );
      if( NULL != methodToCall ) {
        if( [aTargetObject respondsToSelector: methodToCall] ) {
          // method found
          toReturn = methodToCall;
          break;
        } 
      }
    }
  EC_AUTORELEASEPOOL_END
  
  return toReturn;
}
@end