/**
* Copyright (C) 2008-2010, Squale Project - http://www.squale.org
*
* This file is part of Squale.
*
* Squale 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 3 of the
* License, or any later version.
*
* Squale 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 Lesser General Public License
* along with Squale. If not, see <http://www.gnu.org/licenses/>.
*/
package org.squale.squalix.tools.ckjm;
import gr.spinellis.ckjm.ClassMetrics;
import gr.spinellis.ckjm.ClassMetricsContainer;
import gr.spinellis.ckjm.MetricsFilter;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.squale.jraf.commons.exception.JrafDaoException;
import org.squale.squalecommon.daolayer.result.MeasureDAOImpl;
import org.squale.squalecommon.enterpriselayer.businessobject.component.ClassBO;
import org.squale.squalecommon.enterpriselayer.businessobject.component.parameters.ListParameterBO;
import org.squale.squalecommon.enterpriselayer.businessobject.component.parameters.ParametersConstants;
import org.squale.squalecommon.enterpriselayer.businessobject.result.ckjm.CkjmClassMetricsBO;
import org.squale.squalix.core.AbstractTask;
import org.squale.squalix.core.TaskData;
import org.squale.squalix.core.TaskException;
import org.squale.squalix.core.exception.ConfigurationException;
import org.squale.squalix.util.buildpath.BuildProjectPath;
import org.squale.squalix.util.file.ExtensionFileFilter;
import org.squale.squalix.util.file.FileUtility;
import org.squale.squalix.util.parser.JavaParser;
import org.squale.squalix.util.repository.ComponentRepository;
/**
* T�che Ckjm.<br/> Calcule le CBO (couplage entre les classes) des classes du projet � auditer.<br/> La t�che de
* compilation java doit avoir �t� �x�cut�e avant afin que le chemin du r�pertoire contenant les ".class" (CLASSES_DIRS)
* soit pr�sent dans les param�tres temporaires.<br/> La t�che du source manager associ� au projet doit avoir �t�
* �x�cut�e avant afin que le chemin de la vue (VIEW_PATH) soit pr�sent dans les param�tres temporaires.<br/> No path
* in CLASSES_DIRS parameter must contains spaces else ckjm won't work correctly because ckjm considers a classe name
* with space as a jar file following by the classe name to analyze (see ckjm documentation :
* http://www.spinellis.gr/sw/ckjm/doc/oper.html)
*/
public class CkjmTask
extends AbstractTask
{
/**
* Logger
*/
private static final Log LOGGER = LogFactory.getLog( CkjmTask.class );
/** L'extension d'un fichier java */
public static final String JAVA_FILE_EXTENSION = ".java";
/**
* L'extension d'un fichier java compil�
*/
public static final String COMPILED_JAVA_FILE_EXTENSION = ".class";
/** Parser java */
private JavaParser mParser;
/** Aide � la cr�ation de composants */
private ComponentRepository mRepository;
/** La liste des .class � analyser */
private HashSet mFilesToAnalize;
/** Le chemin vers la vue du projet */
private String mViewPath;
/** Les chemins absolus des sources � auditer */
private List mSourcesPaths;
/** Les noms des fichiers qui peuvent �tre persist�s */
private List mIncludedFileNames;
// Cette tache n'intervient pas dans le calcul de la taille max du file system
/**
* Constructeur par defaut
*/
public CkjmTask()
{
mName = "CkjmTask";
mFilesToAnalize = new HashSet();
mIncludedFileNames = new ArrayList();
}
/**
* Analyse les fichiers compil�s afin de calculer pour chacun le CBO associ�.
*
* @throws TaskException si erreur
*/
public void execute()
throws TaskException
{
mParser = new JavaParser( mProject );
mRepository = new ComponentRepository( mProject, getSession() );
// On initialise les attributs de la t�che
try
{
initialize();
// On analyse les fichiers compil�s
ClassMetricsContainer cm = new ClassMetricsContainer();
Iterator filesIt = mFilesToAnalize.iterator();
while ( filesIt.hasNext() )
{
MetricsFilter.processClass( cm, (String) filesIt.next() );
}
// On fait persister les mesures pour les classes visit�es et qui n'appartiennent pas
// � l'un des r�pertoires exclus de la compilation.
persisteMeasures( cm );
}
catch ( Exception e )
{
throw new TaskException( e );
}
}
/**
* Initialise les attributs de la t�che.
*
* @throws ConfigurationException si erreur
*/
private void initialize()
throws ConfigurationException
{
// On r�cup�re les r�pertoires contenant les .class du projet � analyser
// cr�e par la t�che de compilation java
List classesDirs = (List) this.getData().getData( TaskData.CLASSES_DIRS );
if ( classesDirs == null )
{
String message = CkjmMessages.getString( "ckjm.exception.classdir.not_found" ) + TaskData.CLASSES_DIRS;
LOGGER.error( message );
// Lance une exception de configuration
throw new ConfigurationException( message );
}
// On r�cup�re les chemins relatifs des r�pertoires contenant les .java du projet
ListParameterBO sources = (ListParameterBO) mProject.getParameter( ParametersConstants.SOURCES );
if ( sources == null )
{
String message = CkjmMessages.getString( "ckjm.exception.sources.not_found" ) + ParametersConstants.SOURCES;
LOGGER.error( message );
// Lance une exception de configuration
throw new ConfigurationException( message );
}
// On r�cup�re le view_path cr�e par la t�che du source manager en ajoutant un s�parateur
// Unix en bout au cas o�.
mViewPath = (String) mData.getData( TaskData.VIEW_PATH );
if ( null == mViewPath )
{
String message = CkjmMessages.getString( "ckjm.exception.viewpath.not_found" ) + TaskData.VIEW_PATH;
LOGGER.error( message );
// Lance une exception de configuration
throw new ConfigurationException( message );
}
// On construit la liste des chemins absolus vers les sources.
mSourcesPaths = BuildProjectPath.buildProjectPath( mViewPath, sources.getParameters() );
// On g�n�re la liste des fichiers compil�s � analyser
ExtensionFileFilter filter = new ExtensionFileFilter( COMPILED_JAVA_FILE_EXTENSION );
for ( int i = 0; i < classesDirs.size(); i++ )
{
FileUtility.createRecursiveListOfFiles( new File( (String) classesDirs.get( i ) ), filter, mFilesToAnalize );
}
// Les nom des fichiers qui peuvent �tre persist�s
mIncludedFileNames =
FileUtility.getIncludedFiles(
mViewPath,
mSourcesPaths,
(ListParameterBO) mProject.getParameter( ParametersConstants.INCLUDED_PATTERNS ),
(ListParameterBO) mProject.getParameter( ParametersConstants.EXCLUDED_PATTERNS ),
null, new String[] { ".java" } );
// on a pas besoin des r�pertoires exclus de la compilation car on analyse les .class
}
/**
* Fait persister les mesures.
*
* @param pContainer le containeur des mesures ckjm
* @throws IOException si erreur de flux
* @throws JrafDaoException si erreur de persistance
*/
private void persisteMeasures( ClassMetricsContainer pContainer )
throws IOException, JrafDaoException
{
// On parcours l'ensemble des m�triques calcul�es
Set entries = pContainer.getEntries();
CkjmClassMetricsBO ckjmMetric;
for ( Iterator i = entries.iterator(); i.hasNext(); )
{
Map.Entry e = (Map.Entry) i.next();
ClassMetrics classMetrics = (ClassMetrics) e.getValue();
if ( classMetrics.isVisited() && ( MetricsFilter.includeAll() || classMetrics.isPublic() ) )
{
ckjmMetric = new CkjmClassMetricsBO();
ckjmMetric.setTaskName( this.getName() );
ckjmMetric.setAudit( this.getAudit() );
// Le nom de la classe enti�rement qualifi�:
String classNameWithPackage = (String) e.getKey();
// On r�cup�re le nom relatif du fichier source si le fichier
// doit �tre analyser.
String relativeFileName = isInclude( classNameWithPackage );
if ( null != relativeFileName )
{ // La classe appartient bien au projet � auditer
// On r�cup�re la classe
ClassBO classBO = mParser.getClass( classNameWithPackage, relativeFileName );
// On fait persister la classe et on construit la mesure ckjm associ�e
ckjmMetric.setComponent( mRepository.persisteComponent( classBO ) );
ckjmMetric.setWmc( pContainer.getMetrics( classNameWithPackage ).getWmc() );
ckjmMetric.setDit( pContainer.getMetrics( classNameWithPackage ).getDit() );
ckjmMetric.setNoc( pContainer.getMetrics( classNameWithPackage ).getNoc() );
ckjmMetric.setCbo( pContainer.getMetrics( classNameWithPackage ).getCbo() );
ckjmMetric.setRfc( pContainer.getMetrics( classNameWithPackage ).getRfc() );
ckjmMetric.setLcom( pContainer.getMetrics( classNameWithPackage ).getLcom() );
ckjmMetric.setCa( pContainer.getMetrics( classNameWithPackage ).getCa() );
ckjmMetric.setNpm( pContainer.getMetrics( classNameWithPackage ).getNpm() );
// On fait persister la mesure
MeasureDAOImpl.getInstance().create( getSession(), ckjmMetric );
getSession().commitTransactionWithoutClose();
getSession().beginTransaction();
}
}
}
}
/**
* Retourne le nom relatif du fichier source de la classe de nom absolu <code>pAbsoluteClassName</code> si la
* classe fait partie des sources � analyser.
*
* @param pAbsoluteClassName le nom absolu de la classe
* @return le nom relatif du fichier source de la classe, null si la classe ne doit pas �tre auditer.
* @throws IOException si erreur
*/
private String isInclude( String pAbsoluteClassName )
throws IOException
{
String relativeFileName = null;
// On v�rifie que la classe fait partie des fichiers
String packageName = mParser.getAbsolutePackage( pAbsoluteClassName );
// On r�cup�re le nom absolu du fichier compil� associ� � la classe
// NB: On ne v�rifie pas si le nom retourn� est nul car c'est impossible
String classFileName = FileUtility.getClassFileName( mFilesToAnalize, pAbsoluteClassName );
// Puis le nom du fichier source dans lequel se trouve la d�finition de la classe
String fileName = FileUtility.getFileName( classFileName );
// On construit le chemin relatif du fichier source de la classe
String endOfFileName = FileUtility.buildEndOfFileName( packageName, fileName );
// On r�cup�re le nom absolu du fichier source si il est pr�sent
// dans les param�tres.
String absoluteFileName = FileUtility.getAbsoluteFileName( mSourcesPaths, endOfFileName );
if ( mIncludedFileNames.contains( absoluteFileName ) )
{
relativeFileName = FileUtility.getRelativeFileName( absoluteFileName, mViewPath );
}
return relativeFileName;
}
}