// This file is part of the AspectC++ compiler 'ac++'.
// Copyright (C) 1999-2003  The 'ac++' developers (see aspectc.org)
//                                                                
// This program is free software;  you can redistribute it and/or 
// modify it under the terms of the GNU General Public License as 
// published by the Free Software Foundation; either version 2 of 
// the License, or (at your option) any later version.            
//                                                                
// This program 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 General Public License for more details.                   
//                                                                
// You should have received a copy of the GNU General Public      
// License along with this program; if not, write to the Free     
// Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, 
// MA  02111-1307  USA                                            

#include "PointCutContext.h"
#include "TrackerDog.h"
#include "JoinPoint.h"
#include "IntroductionUnit.h"

#include "Puma/ErrorStream.h"
#include "Puma/CFunctionInfo.h"
#include "Puma/CClassInfo.h"
#include "Puma/ACPointcutInfo.h"
#include "Puma/ACAspectInfo.h"
#include "Puma/CClassDatabase.h"
#include "Puma/CTree.h"
#include "Puma/Filter.h"

void PointCutContext::setup(CTranslationUnit& tunit) {
  scopes (tunit);
  TrackerDog tracker (tunit, _world);
  tracker.run ();
}

void PointCutContext::scopes (CTranslationUnit& tunit) {
  CClassDatabase &db = tunit.db ();

  for (unsigned int c = 0; c < db.ClassInfos (); c++) {
    CClassInfo *ci = db.ClassInfo (c);

    // Don't consider
    // * anonymous classes like template instances(?)
    // * the generated class JoinPoint
    // * classes defined in the special namespace AC
    if (strncmp (ci->Name (), "%", 1) == 0 ||
        strcmp (ci->QualName (), "JoinPoint") == 0 ||
        strncmp (ci->QualName (), "AC::", 4) == 0)
      continue;

    // Templates and classes nested in template class are not considered for
    // matching, only instances
    CObjectInfo *oi = ci;
    bool in_template = false;
    do {
      assert (oi->ClassInfo ());
      if (oi->ClassInfo ()->insideTemplate ()) {
        in_template = true;
        break;
      }
      oi = oi->NextObject ();
    } while (oi != ci);
    if (in_template)
      continue;
      
    ci = (CClassInfo*)TrackerDog::unique_object (ci);
    if (ci) {
      ACAspectInfo *ai = db.AspectInfo (ci);
      if (ai)
        _world.new_aspect (ai);
      else
        _world.new_class (ci);
    }
  }

  for (unsigned f = 0; f < db.FunctionInfos (); f++) {
    CFunctionInfo *fi = db.FunctionInfo (f);
    fi = TrackerDog::filter_func (fi);
    if (!fi)
      continue;
    fi = (CFunctionInfo*)TrackerDog::unique_object (fi);
    if (fi)
      _world.new_function (fi);
  }
}

CFunctionInfo *PointCutContext::lookup_pointcut(ACAspectInfo *ai,
                                                CFunctionInfo *fi) {
  CFunctionInfo *result = 0;
  const char *name = fi->Name ();
  CClassDatabase *db = (CClassDatabase*)fi->ClassDB ();
  
  // think about it!
  // What happens with 3 level aspect hierarchies?
  // What about overloading?
  // What about fully qualified pointcut names? 
  for (int p = 0; p < ai->PointcutInfos (); p++) {
    ACPointcutInfo *pi = ai->PointcutInfo (p);
    if (strcmp (pi->name (), name) == 0) {
      result = pi->function ();
      break;
    }
  }
  
  // if the pointcut is not defined in this aspect it has to be defined in a
  // base aspect that is derived from the aspect in which the virtual pointcut
  // is defined
  if (!result) {
    // the aspect/class in which we are searching
    CClassInfo *ci = ai->ClassInfo ();

    // the class where the referenced pointcut is located in ...
    CClassInfo *ref_class = fi->ClassScope ()->ClassInfo ();

    // search the function in base aspects
    for (int b = ci->BaseClasses () - 1; b >= 0; b--) {
      CClassInfo *bci   = ci->BaseClass (b)->Class ();
      ACAspectInfo *bai = db->AspectInfo (bci);
      // the class is relevant if it is an aspect an derived from ref_class
      if (bai && bci->isBaseClass (ref_class, true)) {
        result = lookup_pointcut (bai, fi);
        if (result)
          break;
      }
    }
  }
  
  return result;
}

 
CFunctionInfo *PointCutContext::lookup_pointcut (CFunctionInfo *func,
						 ErrorStream &err, 
						 CTree *node) {
  CFunctionInfo *result = func; // default: out = in
  const char *name = func->Name ();
  CClassDatabase *db = (CClassDatabase*)func->ClassDB ();

  // check if the name really corresponds to a pointcut
  if (!db->PointcutInfo (func)) {
    err << sev_error << node->token ()->location ()
	<< "'" << name << "' is no named pointcut" 
	<< endMessage;
    return 0;
  }

  // everything ok, if the pointcut is not member of a class or aspect
  if (!func->ClassScope ())
    return result;

  // the class where the referenced pointcut is located in ...
  CClassInfo *ref_class = func->ClassScope ()->ClassInfo ();

  // if the pointcut is declared virtual, find the real definition
  if (ref_class != _aspect->ClassInfo () && func->isVirtual ()) {
    result = lookup_pointcut (_aspect, func);
    if (!result)
      result = func;
  }
	
  if (result->isPureVirtual ()) {
    err << sev_error << node->token ()->location ()
	<< "no definition for pure virtual pointcut '" << name << "'"
	<< endMessage;
    return 0;
  }

  return result;
}

bool PointCutContext::in_project (CObjectInfo *obj) {
  // built-in (generated constructors/destructors belong to the project if
  // the corresponding class belongs to the project
  if (obj->isBuiltin () && obj->FunctionInfo () &&
    (obj->FunctionInfo ()->isConstructor () ||
     obj->FunctionInfo ()->isDestructor ())) {
    Unit *unit = obj->FunctionInfo ()->Record ()->SourceInfo ()->SrcUnit ();
    return obj->ClassDB()->Project ()->isBelow (unit) ||
           IntroductionUnit::cast (unit);
  }
  Unit *unit = obj->SourceInfo ()->SrcUnit ();
  return obj->ClassDB()->Project ()->isBelow (unit) ||
         IntroductionUnit::cast (unit);
}
