//==================================================================================================
// M e t a _ m o d e l                                                                    Interface
// D e p e n d e n c y _ e x t r a c t i o n
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ 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 (http://www.gnu.org).

/*DESCRIPTION*/
/* This module provides facilities to determine the dependency between source files. */

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "meta_model/dependency_extraction.hpp"

// Guardian //--------------------------------------------------------------------------------------
#ifndef guMetaModelDependencyExtraction
#define guMetaModelDependencyExtraction

// Headers //---------------------------------------------------------------------------------------
#include <bpp/meta_model/structure.hpp> /*INCLUDE*/

namespace bpp {

// Importation/Exportation //-----------------------------------------------------------------------
#ifdef META_MODEL_DLL
 #define dll_export DLL_EXPORT
#else
 #define dll_export DLL_IMPORT
#endif

// Namespaces //------------------------------------------------------------------------------------
#define public_area  metaModelDependencyExtraction
#define private_area metaModelDependencyExtraction_private

namespace public_area  { /*NAMESPACE*/ using namespace metaModelStructure; }
namespace private_area { using namespace public_area; }

extern_module_name;

// Initialization //--------------------------------------------------------------------------------
#define iniMetaModelDependencyExtraction
has_initializer;

// Macrocommands //---------------------------------------------------------------------------------

// Types & Classes //-------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area { class clFolderProcessingDependency; }

// Functions Interface //---------------------------------------------------------------------------
namespace public_area {
 function void getDependency(clFileS &,tcString);
 function void getDependency(clDependency &,tyBoolean=true);
}

namespace private_area {
 function void makeRecursiveDependency(clDependency &);
 function void makeRecursiveDependency(clDependency &,clFileDependency &);
}

// Errors //----------------------------------------------------------------------------------------
namespace public_area {}

// Constants & Variables //-------------------------------------------------------------------------
extern_static_constant(private,tcString,goClassPathAlias,?);
extern_static_constant(private,tcString,goLibraryPathAlias,?);
extern_static_constant(private,tcString,goObjectPathAlias,?);

// F o l d e r P r o c e s s i n g D e p e n d e n c y  Interface //--------------------------------
namespace private_area {
 class clFolderProcessingDependency : public clFolderProcessing {
  //-----------------------------------------------------------------------------------------Private
  private_property clString       atBlankSpace;
  private_property clString       atPath;
  private_property clString       atName;
  private_property clDependency & atDependency;
  private_property tcString       atPathAlias;
  private_property tyBoolean      atDisplayed;

  private_property constructor clFolderProcessingDependency(const clFolderProcessingDependency &);
  private_property clFolderProcessingDependency & operator = (const clFolderProcessingDependency &);
  //------------------------------------------------------------------------------------------Public
  public_property constructor clFolderProcessingDependency(clDependency &,tcString,tyBoolean=true);

  public_property virtual destructor clFolderProcessingDependency(void) {}

  public_property void startFolder(ctString &);
  public_property void endFolder(ctString &);
  public_property void file(ctString &);
  public_property void unknown(ctString &);
 };
}

// Functions Inline //------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// X X X  Inline //---------------------------------------------------------------------------------
namespace {}

// End //-------------------------------------------------------------------------------------------
}
#undef dll_export
#undef public_area
#undef private_area
#endif
 
//==================================================================================================
// M e t a _ m o d e l                                                               Implementation
// D e p e n d e n c y _ e x t r a c t i o n
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ 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 (http://www.gnu.org).

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "meta_model/dependency_extraction.cpp"

// DLL Belonging //---------------------------------------------------------------------------------
#define META_MODEL_DLL

// Headers //---------------------------------------------------------------------------------------
#include <bpp/meta_model/dependency_extraction.hpp> /*INTERFACE*/

namespace bpp {

// Namespaces //------------------------------------------------------------------------------------
#define public_area  metaModelDependencyExtraction
#define private_area metaModelDependencyExtraction_private
#define dll_export   DLL_EXPORT

namespace public_area  {}
namespace private_area {}

static_module_name("Meta_model/Dependency_extraction");

// Initialization //--------------------------------------------------------------------------------
#undef iniMetaModelDependencyExtraction
static_constant(private_area::clInitializer,goInitializer);

// Errors //----------------------------------------------------------------------------------------
namespace public_area {}

// Constants & Variables //-------------------------------------------------------------------------
static_constant(tcString,goClassPathAlias);
static_constant(tcString,goLibraryPathAlias);
static_constant(tcString,goObjectPathAlias);

// Static Members //--------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// Functions Implementation //----------------------------------------------------------------------
namespace public_area {
 //------------------------------------------------------------------------------------GetDependency
 /*FUNCTION*/ /* Finds the files a given C++ source file depends on. */
 function void getDependency(clFileS & agFileS,tcString agFileName) {
  clInFile lcFile;
  clString lcString;

  open(lcFile,agFileName,ios::in);
  lcFile >> lcString;

  while (lcString!="") {
   if (lcString=="#include") {
    lcFile >> lcString;

    if (lcString.size()>2)
     if (lcString[0]=='<' and lastCharacter(lcString)=='>')
      agFileS.insert(lcString.part(1,lcString.size()-2));
   }

   lcFile >> lcString;
  }

  close(lcFile);
 }
 //------------------------------------------------------------------------------------GetDependency
 /*FUNCTION*/ /* Finds the dependency of each C++ source file in given folders. */
 function void getDependency(clDependency & agDependency,tyBoolean agDisplayed) {
  clAliasX::const_iterator lcCurrentLibrary = agDependency.paths().begin();
  clAliasX::const_iterator lcLastLibrary    = agDependency.paths().end();

  private_area::clFolderProcessingDependency * lcProcessing;

  while (lcCurrentLibrary!=lcLastLibrary) {
   if (agDisplayed) {
    environment::nextLine();
    environment::inform("Searching Dependency In '",false);
    environment::out((*lcCurrentLibrary).first.data());
    environment::out("'...",true);
   }

   lcProcessing=new_object(private_area::clFolderProcessingDependency(agDependency,
                           (*lcCurrentLibrary).first.data(),agDisplayed));

   scanFolder((*lcCurrentLibrary).second.data(),*lcProcessing);
   delete_object(lcProcessing);
   lcCurrentLibrary++;
  }

  private_area::makeRecursiveDependency(agDependency);
 }
}

namespace private_area {
 //--------------------------------------------------------------------------MakeRecursiveDependency
 function void makeRecursiveDependency(clDependency & agDependency) {
  clFileDependencyX::iterator lcCurrentFile = agDependency.files().begin();
  clFileDependencyX::iterator lcLastFile    = agDependency.files().end();

  clFileDependency * lcFile;

  while (lcCurrentFile!=lcLastFile) {
   lcFile=&(*lcCurrentFile).second;
   if (not lcFile->complete()) makeRecursiveDependency(agDependency,*lcFile);
   lcCurrentFile++;
  }
 }
 //--------------------------------------------------------------------------MakeRecursiveDependency
 function void makeRecursiveDependency(clDependency & agDependency,clFileDependency & agFile) {
  clFileS::const_iterator lcCurrentFile1 = agFile.files().begin();
  clFileS::const_iterator lcLastFile1    = agFile.files().end();

  clFileS::const_iterator lcCurrentFile2;
  clFileS::const_iterator lcLastFile2;
  clFileS                 lcList;
  clFileDependency *      lcFile;

  while (lcCurrentFile1!=lcLastFile1) {
   if (agDependency.files().count(*lcCurrentFile1)==1) {
    lcFile=&(agDependency.files()[*lcCurrentFile1]);
    if (not lcFile->complete()) makeRecursiveDependency(agDependency,*lcFile);
    lcCurrentFile2=lcFile->files().begin();
    lcLastFile2=lcFile->files().end();

    while (lcCurrentFile2!=lcLastFile2) {
     lcList.insert(*lcCurrentFile2);
     lcCurrentFile2++;
    }
   }

   lcCurrentFile1++;
  }

  lcCurrentFile1=lcList.begin();
  lcLastFile1=lcList.end();

  while (lcCurrentFile1!=lcLastFile1) {
   if (agFile.alias()!=(*lcCurrentFile1)) agFile.files().insert(*lcCurrentFile1);
   lcCurrentFile1++;
  }

  agFile.complete()=true;
 }
}

// F o l d e r P r o c e s s i n g D e p e n d e n c y  Implementation //---------------------------
namespace private_area {
 //--------------------------------------------------------------------------------------Constructor
 property clFolderProcessingDependency::
 clFolderProcessingDependency(clDependency & agDependency,tcString agPathAlias,
                              tyBoolean agDisplayed)
 : atBlankSpace(),atPath(),atName(),atDependency(agDependency),atPathAlias(agPathAlias),
   atDisplayed(agDisplayed) {}
 //--------------------------------------------------------------------------------------StartFolder
 property void clFolderProcessingDependency::startFolder(ctString & agFullName) {
  clLibraryDependency lcDependency;
  clString            lcPath;
  clString            lcShortName;

  extractShortName(lcPath,lcShortName,agFullName);

  if (atPath=="") {
   atPath=lcShortName;
   atName=lcShortName;
  }
  else {
   atPath+="/"+lcShortName;
   atName+="_"+lcShortName;
  }

  if (atDisplayed) {
   environment::out(atBlankSpace.data(),false,true);
   environment::out(atPath.data());
   environment::out(":",true);
  }

  lcDependency.name()=clString("$(")+private_area::goLibraryPathAlias+")/lib"+atName+"."
                      +libraryExtension();

  lcDependency.dllName()=clString("$(")+private_area::goLibraryPathAlias+")/"+dllPrefix()
                         +atName+"."+dllExtension();

  lcDependency.alias()=atName;
  lcDependency.dllAlias()=atName+"_dll";
  atDependency.libraries().insert(std_make_pair(atPath,lcDependency));
  atBlankSpace+=" ";
 }
 //----------------------------------------------------------------------------------------EndFolder
 property void clFolderProcessingDependency::endFolder(ctString & agFullName) {
  clString lcPath;
  clString lcShortName;

  tyCardinal lcCounter = atPath.size()-1;

  extractShortName(lcPath,lcShortName,agFullName);
  while (lcCounter>0 and atPath[lcCounter]!='/') lcCounter--;
  atPath.resize(lcCounter);
  atName.resize(lcCounter);
  atBlankSpace=atBlankSpace.part(0,atBlankSpace.size()-1);
 }
 //---------------------------------------------------------------------------------------------File
 property void clFolderProcessingDependency::file(ctString & agFullName) {
  clString           lcAlias;
  clString           lcBody;
  tyCardinal         lcCounter;
  clString           lcCppObject;
  clString           lcExtension;
  clFileDependency * lcInfo;
  clString           lcJavaObject;
  clString           lcPath;
  clString           lcShortName;
  clString           lcString;

  extractShortName(lcPath,lcShortName,agFullName);
  separateFileName(lcBody,lcExtension,lcShortName);

  if (lcExtension==hppExtension() or lcExtension==cppExtension() or lcExtension=="java") {
   if (atPath=="") {
    lcAlias=lcShortName;
    lcCppObject=lcBody+"."+objectExtension();
    lcJavaObject=lcBody+".class";
   }
   else {
    lcAlias=atPath+"/"+lcShortName;
    lcCppObject=atPath+"/"+lcBody+"."+objectExtension();
    lcJavaObject=atPath+"/"+lcBody+".class";
   }

   if (atDisplayed) {
    environment::out(atBlankSpace.data(),false,true);
    environment::out(lcAlias.data(),true);
   }

   atDependency.files().insert(std_make_pair(lcAlias,clFileDependency()));
   lcInfo=&(atDependency.files()[lcAlias]);
   lcInfo->alias()=lcAlias;
   lcInfo->sourceName()=clString("$(")+atPathAlias+")/"+lcAlias;

   if (lcExtension==cppExtension()) {
    lcInfo->objectName()=clString("$(")+private_area::goObjectPathAlias+")/"+lcCppObject;
    atDependency.libraries()[atPath].files().insert(lcAlias);
    getDependency(lcInfo->files(),agFullName.data());
   }
   else if (lcExtension=="java") {
    lcInfo->isJava()=true;
    lcInfo->objectName()=clString("$(")+private_area::goClassPathAlias+")/"+lcJavaObject;
    lcPath=atPath;
    lcCounter=(lcPath.size()>1 ? lcPath.size()-1 : 0);

    while (lcCounter>0) {
     if (lcPath.data()[lcCounter]=='/') {
      lcString=lcPath.part(0,lcCounter);
      if (lcString!="") atDependency.libraries()[lcString].files().insert(lcAlias);
     }

     --lcCounter;
    }

    atDependency.libraries()[atPath].files().insert(lcAlias);
   }
   else getDependency(lcInfo->files(),agFullName.data());
  }
 }
 //------------------------------------------------------------------------------------------Unknown
 property void clFolderProcessingDependency::unknown(ctString &) {}
}

// I n i t i a l i z e r  Implementation //---------------------------------------------------------
namespace private_area {
 //--------------------------------------------------------------------------------------------Start
 property void clInitializer::start(void) {
  if (atCounter++ == 0) {
   try {
    #include <bpp/modules.hpp> /*NEED*/
    registerStop(this);
    environment::informInitialization(goModuleName);

    goClassPathAlias   = "CLASS_PATH";
    goLibraryPathAlias = "LIBRARY_PATH";
    goObjectPathAlias  = "OBJECTS_PATH";
   }

   initializer_catch;
  }
 }
 //---------------------------------------------------------------------------------------------Stop
 property void clInitializer::stop(void) { environment::informTermination(goModuleName); }
}

// End //-------------------------------------------------------------------------------------------
}