/* 
   EOJoin.m

   Copyright (C) 1996 Free Software Foundation, Inc.

   Author: Ovidiu Predescu <ovidiu@bx.logicnet.ro>
   Date: August 1996

   This file is part of the GNUstep Database Library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License as published by the Free Software Foundation; either
   version 2 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
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public
   License along with this library; see the file COPYING.LIB.
   If not, write to the Free Software Foundation,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#include <Foundation/NSString.h>
#include <Foundation/NSUtilities.h>

#include <eoaccess/common.h>
#include <eoaccess/EOModel.h>
#include <eoaccess/EOEntity.h>
#include <eoaccess/EOAttribute.h>
#include <eoaccess/EORelationship.h>
#include <eoaccess/EOJoin.h>

@implementation EOJoin

- initWithSourceAttribute:(EOAttribute*)source
    destinationAttribute:(EOAttribute*)destination
    joinOperator:(EOJoinOperator)_joinOperator
    joinSemantic:(EOJoinSemantic)_joinSemantic
{
    sourceAttribute = source;
    destinationAttribute = destination;
    joinOperator = _joinOperator;
    joinSemantic = _joinSemantic;
    return self;
}

- (void)dealloc
{
	[docComment release];
    [super dealloc];
}

- (void)gcDecrementRefCountOfContainedObjects
{
    [sourceAttribute gcDecrementRefCount];
    [destinationAttribute gcDecrementRefCount];
    [relationship gcDecrementRefCount];
}

- (BOOL)gcIncrementRefCountOfContainedObjects
{
    if(![super gcIncrementRefCountOfContainedObjects])
	return NO;

    [sourceAttribute gcIncrementRefCount];
    [destinationAttribute gcIncrementRefCount];
    [relationship gcIncrementRefCount];

    [sourceAttribute gcIncrementRefCountOfContainedObjects];
    [destinationAttribute gcIncrementRefCountOfContainedObjects];
    [relationship gcIncrementRefCountOfContainedObjects];

    return YES;
}

// Is equal only if same sourceAttribute; used to make aliasing ordering stable
- (unsigned)hash
{
    return [sourceAttribute hash];
}

-(NSString*)description
{
  NSString* dscr=nil;
  NSString* joinOperatorDescr=nil;
  NSString* joinSemanticDescr=nil;
  joinOperatorDescr=[self joinOperatorDescription];
  joinSemanticDescr=[self joinSemanticDescription];

  dscr=[NSString stringWithFormat:@"<%s %p -",
				 object_get_class_name(self),
				 (void*)self];
  dscr=[dscr stringByAppendingFormat:@" sourceAttribute=%@",
			  [sourceAttribute name]];
  dscr=[dscr stringByAppendingFormat:@" destinationAttribute=%@",
			 [destinationAttribute name]];
  dscr=[dscr stringByAppendingFormat:@" relationship name=%@",
			 [relationship name]];
  dscr=[dscr stringByAppendingFormat:@" joinOperator=%@ joinSemantic=%@>",
			 joinOperatorDescr,
			 joinSemanticDescr];
  return dscr;
};

-(NSString*)joinOperatorDescription
{
  switch(joinOperator)
	{
	case EOJoinEqualTo:
	  return @"EOJoinEqualTo";
	case EOJoinNotEqualTo:
	  return @"EOJoinNotEqualTo";
	case EOJoinGreaterThan:
	  return @"EOJoinGreaterThan";
	case EOJoinGreaterThanOrEqualTo:
	  return @"EOJoinGreaterThanOrEqualTo";
	case EOJoinLessThan:
	  return @"EOJoinLessThan";
	case EOJoinLessThanOrEqualTo:
	  return @"EOJoinLessThanOrEqualTo";
	};
  return nil;
};

-(NSString*)joinSemanticDescription
{
  switch(joinSemantic)
	{
	case EOInnerJoin:
	  return @"EOInnerJoin";
	case EOFullOuterJoin:
	   return @"EOFullOuterJoin";
	case EOLeftOuterJoin:
	  return @"EOLeftOuterJoin";
	case EORightOuterJoin:
	  return @"EORightOuterJoin";
	};
  return nil;
};

- (void)setDestinationAttribute:(EOAttribute*)attribute
{
    ASSIGN(destinationAttribute, attribute);
}

- (void)setSourceAttribute:(EOAttribute*)attribute
{
    ASSIGN(sourceAttribute, attribute);
}

- (void)setRelationship:(EORelationship*)_relationship
{
    ASSIGN(relationship, _relationship);
}

- (void)setJoinOperator:(EOJoinOperator)jo
{
    joinOperator = jo;
}

- (void)setJoinSemantic:(EOJoinSemantic)js
{
    joinSemantic = js;
}

- (void)setDocComment:(NSString*)comment
{
    ASSIGN(docComment,comment);
}

- (EOAttribute*)destinationAttribute	{ return destinationAttribute; }
- (EOAttribute*)sourceAttribute		{ return sourceAttribute; }
- (EOJoinOperator)joinOperator		{ return joinOperator; }
- (EOJoinSemantic)joinSemantic		{ return joinSemantic; }
- (EORelationship*)relationship		{ return relationship; }
- (NSString*)docComment { return docComment; }

@end /* EOJoin */


@implementation EOJoin (EOJoinPrivate)

+ (EOJoin*)joinFromPropertyList:(id)propertyList
{
    EOJoin* join = [[EOJoin new] autorelease];
    NSString* joinOperatorPList;
    NSString* joinSemanticPList;
    EOJoinOperator jo;
    EOJoinSemantic js;

    [join setSourceAttribute:[propertyList objectForKey:@"sourceAttribute"]];
    [join setDestinationAttribute:
	    [propertyList objectForKey:@"destinationAttribute"]];

    joinOperatorPList = [propertyList objectForKey:@"joinOperator"];
    if([joinOperatorPList isEqual:@"EOJoinEqualTo"])
	jo = EOJoinEqualTo;
    else if([joinOperatorPList isEqual:@"EOJoinNotEqualTo"])
	jo = EOJoinNotEqualTo;
    else if([joinOperatorPList isEqual:@"EOJoinGreaterThan"])
	jo = EOJoinGreaterThan;
    else if([joinOperatorPList isEqual:@"EOJoinGreaterThanOrEqualTo"])
	jo = EOJoinGreaterThanOrEqualTo;
    else if([joinOperatorPList isEqual:@"EOJoinLessThan"])
	jo = EOJoinLessThan;
    else if([joinOperatorPList isEqual:@"EOJoinLessThanOrEqualTo"])
	jo = EOJoinLessThanOrEqualTo;
    else
	jo = -1;
    [join setJoinOperator:jo];

    joinSemanticPList = [propertyList objectForKey:@"joinSemantic"];
    if([joinSemanticPList isEqual:@"EOInnerJoin"])
	js = EOInnerJoin;
    else if([joinSemanticPList isEqual:@"EOFullOuterJoin"])
	js = EOFullOuterJoin;
    else if([joinSemanticPList isEqual:@"EOLeftOuterJoin"])
	js = EOLeftOuterJoin;
    else if([joinSemanticPList isEqual:@"EORightOuterJoin"])
	js = EORightOuterJoin;
    else
	js = -1;
    [join setJoinSemantic:js];
	[join setDocComment:[propertyList objectForKey:@"docComment"]];

    return join;
}

- (void)replaceStringsWithObjectsInRelationship:(EORelationship*)_relationship
{
    EOEntity* entity = [_relationship entity];
    EOEntity* destinationEntity = [_relationship destinationEntity];
    EOModel* model = [entity model];
    EOAttribute* attribute;

    attribute = [entity attributeNamed:(NSString*)sourceAttribute];
    if(attribute)
	[self setSourceAttribute:attribute];
    else {
	  NSString* _error=[NSString stringWithFormat:@"invalid attribute name '%@' specified as source attribute for "
								 @"join in relationship '%@' in entity '%@'",
								 sourceAttribute, [_relationship name], [entity name]];
	  [model errorInReadingWithError:_error];
    }

    attribute = [destinationEntity attributeNamed:
					(NSString*)destinationAttribute];
    if(attribute)
	[self setDestinationAttribute:attribute];
    else {
	  NSString* _error=[NSString stringWithFormat:@"invalid attribute name '%@' specified as destination "
	      @"attribute for join in relationship '%@' in entity '%@'",
								 destinationAttribute, [_relationship name], [entity name]];
	  [model errorInReadingWithError:_error];
    }

    if(joinOperator == -1) {
	  NSString* _error=[NSString stringWithFormat:@"invalid join operator in relationship '%@' in entity '%@'",
								 [_relationship name], [entity name]];
	  [model errorInReadingWithError:_error];
    }

    if(joinSemantic == -1) {
	  NSString* _error=[NSString stringWithFormat:@"invalid join semantic in relationship '%@' in entity '%@'",
								 [_relationship name], [entity name]];
	  [model errorInReadingWithError:_error];
    }
}

- (id)propertyList
{
    id propertyList = [[NSMutableDictionary new] autorelease];
    id joinOperatorPList[] = { @"EOJoinEqualTo", @"EOJoinNotEqualTo",
			@"EOJoinGreaterThan", @"EOJoinGreaterThanOrEqualTo",
			@"EOJoinLessThan", @"EOJoinLessThanOrEqualTo", nil };
    id joinSemanticPList[] = { @"EOInnerJoin", @"EOFullOuterJoin",
			@"EOLeftOuterJoin", @"EORightOuterJoin", nil };

    if(sourceAttribute)
	[propertyList setObject:[sourceAttribute name]
		      forKey:@"sourceAttribute"];
    if(destinationAttribute)
	[propertyList setObject:[destinationAttribute name]
		      forKey:@"destinationAttribute"];
    [propertyList setObject:joinOperatorPList[joinOperator]
		  forKey:@"joinOperator"];
    [propertyList setObject:joinSemanticPList[joinSemantic]
		  forKey:@"joinSemantic"];
    if(docComment)
	[propertyList setObject:docComment forKey:@"docComment"];

    return propertyList;
}

@end /* EOJoin (EOJoinPrivate) */
