/** * 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.jdepend; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.regex.PatternSyntaxException; import jdepend.framework.JDepend; import jdepend.framework.JavaPackage; import jdepend.framework.PackageFilter; import org.squale.squalecommon.daolayer.result.MeasureDAOImpl; import org.squale.squalecommon.enterpriselayer.businessobject.component.PackageBO; 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.jdepend.JDependPackageMetricsBO; import org.squale.squalix.core.AbstractTask; import org.squale.squalix.core.TaskData; import org.squale.squalix.core.TaskException; import org.squale.squalix.util.buildpath.BuildProjectPath; import org.squale.squalix.util.file.FileUtility; import org.squale.squalix.util.parser.JavaParser; import org.squale.squalix.util.repository.ComponentRepository; /** * T�che JDepend pour l'analyse des packages java */ public class JDependTask extends AbstractTask { /** Aide � la cr�ation de composants */ private ComponentRepository mRepository; /** l'outil jdepend */ private JDepend mJDepend; /** le filtre pour construire les packages valides */ private JDependFilter mFilter; /** l'ensemble des packages que l'on consid�re comme valides */ private ArrayList mValidPackages; /** * liste des sources � analyser (en tenant compte des exclusions et inclusions, (r�cup�r�es car r�utilis�es) */ private List mIncludedFileNames = new ArrayList( 0 ); // Cette tache n'intervient pas dans le calcul de la taille du file system /** * Constructeur par defaut */ public JDependTask() { mName = "JDependTask"; mValidPackages = new ArrayList(); mFilter = new JDependFilter(); } /** * {@inheritDoc} * * @see org.squale.squalix.core.Task#execute() */ public void execute() throws TaskException { try { // Le chemin racine du projet (vue clearcase, ...) String viewPath = (String) mData.getData( TaskData.VIEW_PATH ); // la liste des sources List srcs = (List) ( (ListParameterBO) mProject.getParameters().getParameters().get( ParametersConstants.SOURCES ) ).getParameters(); // Les nom des fichiers qui peuvent �tre persist�s // (on ne veut pour cette t�che que les .java) mIncludedFileNames = FileUtility.getIncludedFiles( viewPath, BuildProjectPath.buildProjectPath( viewPath, srcs ), (ListParameterBO) mProject.getParameter( ParametersConstants.INCLUDED_PATTERNS ), (ListParameterBO) mProject.getParameter( ParametersConstants.EXCLUDED_PATTERNS ), (ListParameterBO) mProject.getParameter( ParametersConstants.EXCLUDED_DIRS ), new String[] { ".java" } ); // On va filtrer les packages de l'api java pour les exclure de l'analyse PackageFilter filter = new PackageFilter(); filter.addPackage( "java.*" ); filter.addPackage( "javax.*" ); filter.addPackage( "org.ietf.jgss" ); filter.addPackage( "org.omg.*" ); filter.addPackage( "org.w3c.*" ); filter.addPackage( "org.xml.*" ); mJDepend = new JDepend( filter ); mRepository = new ComponentRepository( mProject, getSession() ); // on r�cup�re les r�pertoires o� se trouvent les .class List classesDirs = (List) mData.getData( TaskData.CLASSES_DIRS ); // on affecte ces r�pertoires comme �tant les r�pertoires // racine pour l'analyse for ( int i = 0; i < classesDirs.size(); i++ ) { mJDepend.addDirectory( (String) classesDirs.get( i ) ); } // r�cup�re l'ensemble des packages d�finis pour l'arborescence dont // on a d�fini la racine ci dessus Collection packages = mJDepend.analyze(); Iterator iter = packages.iterator(); JDependPackageMetricsBO measure; Collection measuresColl = new ArrayList( 0 ); JavaParser parser = new JavaParser( mProject ); // on cr�� d'abord la liste des packages � prendre en compte // en mettant la chaine vide comme parent root // on lance la construction des r�pertoires valides for ( int i = 0; i < classesDirs.size(); i++ ) { buildValidPackagesSet( new File( (String) classesDirs.get( i ) ), "" ); } while ( iter.hasNext() ) { JavaPackage p = (JavaPackage) iter.next(); if ( mValidPackages.contains( p.getName() ) ) { measure = new JDependPackageMetricsBO(); // on affecte toutes les mesures measure.setCa( p.afferentCoupling() ); measure.setCe( p.efferentCoupling() ); measure.setNumberOfClassesAndInterfaces( p.getClassCount() ); // pour les metrics flottantes, // on prend des valeurs enti�res en pourcentage (x100) final int TOINT = 100; measure.setAbstractness( p.abstractness() * TOINT ); measure.setDistance( p.distance() * TOINT ); measure.setInstability( p.instability() * TOINT ); // gestion du cycle �ventuel measure.setHaveCycles( p.containsCycle() ); List list = new ArrayList( 0 ); if ( p.containsCycle() ) { // collecte les cycles et les mets dans la liste p.collectCycle( list ); // pour faciliter l'affichage on a une m�thode interm�diaire // qui met en forme measure.setCycle( getCycle( list ) ); } // cr�e le composant et le fait persister PackageBO packBO = parser.getPackage( p.getName() ); measure.setComponent( mRepository.persisteComponent( packBO ) ); measure.setAudit( mAudit ); measure.setTaskName( mName ); measuresColl.add( measure ); } } // persiste l'ensemble des mesures MeasureDAOImpl.getInstance().saveAll( getSession(), measuresColl ); } catch ( Exception e ) { throw new TaskException( e ); } } /** * M�thode qui cr�e la liste des packages consid�r�s comme valide, c'est � dire qu'ils font partie de l'arborescence * directe du r�pertoire racine � analyser. Par cons�quent on ne prend pas en compte les packages "java.*" ... * * @param pRootFile le fichier racine de l'arborescence � parcourir * @param pParent le nom du package parent * @throws IOException en cas de probl�mes de lecture du fichier */ private void buildValidPackagesSet( File pRootFile, String pParent ) throws IOException { // pRootFile est forc�ment un r�pertoire // grace au filtre pour les appels r�cursifs et par appel originel aussi if ( pRootFile.canRead() ) { // On r�cup�re tous les dossiers et les fichiers correspondant au pattern // des fichiers � analyser File[] list = pRootFile.listFiles( mFilter ); // il faut v�rifier l'appartenance aux sources meme si on sait que le parent pr�sum� // appartenait aux sources car il y a des packages communs � plusieurs projets // ("com" par exemple) et il faut etre sur que le parent du package courant // qui appartient aux sources // ou si il n'y a pas de r�pertoire en dessous // ou si il y a pas que des repertoires en dessous // et que le repertoire courant fait partie des sources if ( ( ( list.length == 0 || ( list.length != 0 && pRootFile.listFiles().length != 0 ) ) && isMemberOfParameterList( mIncludedFileNames, pParent.replace( '.', '/' ) ) ) ) { // on ajoute l'ensemble des r�pertoires de l'arborescence mValidPackages.add( pParent ); } String filename = null; for ( int i = 0; i < list.length; i++ ) { // pour m�moriser le parent courant afin de le conserver // pour faire l'appel r�cursif sur chaque enfant String newParent = pParent; filename = list[i].getName(); File f = new File( filename ); // il ne faut pas ajouter un "." au d�but if ( !newParent.equals( "" ) ) { newParent += "." + filename; } else { newParent = filename; } // et on recommence sur chaque fils buildValidPackagesSet( list[i], newParent ); // r�attribue la bonne valeur newParent = pParent; } } } /** * indique si le fichier ou le r�pertoire dont le nom est pSuffix appartient � l'ensemble des sources ou des * r�pertoires exclus (suivant la liste pass�e en argument) en effectuant une recherche r�cursive. * * @param pSuffix le nom du package transform� dont les "." ont �t� remplac�s par des "/" * @param pList la liste des noms absolus des fichiers le cas d'utilisation * @return un bool�en correspondant */ private boolean isMemberOfParameterList( List pList, String pSuffix ) { boolean result = false; for ( int i = 0; !result && !result && i < pList.size(); i++ ) { // src ne peut �tre que le nom d'un fichier String src = (String) pList.get( i ); // on teste l'appartenance � la fois des r�pertoires ou des fichiers try { if ( !pSuffix.equals( "" ) && src.matches( ".*/" + pSuffix + "/.*\\..*" ) ) { // on a trouv� le fichier ou le r�pertoire dans l'arborescence result = true; } } catch ( PatternSyntaxException pse ) { // Il ne peut donc pas s'agir d'un package result = false; } } return result; } /** * @return l'outil jDepend utilis� */ public JDepend getJDepend() { return mJDepend; } /** * @param pJDepend le nouveau JDepend */ public void setJDepend( JDepend pJDepend ) { mJDepend = pJDepend; } /** * @param pList la liste des packages * @return le cycle sous une forme affichable */ private String getCycle( List pList ) { final int MAX_LEN = 2000; String result = ""; // le package qui est la cause du cycle String cycleName = ( (JavaPackage) pList.get( pList.size() - 1 ) ).getName(); for ( int i = 0; i < pList.size() && result.length() < MAX_LEN; i++ ) { JavaPackage pkg = (JavaPackage) pList.get( i ); String name = pkg.getName(); // juste de la mise en forme d'affichage // suivant la fa�on dont JDepend r�cup�re les informations // cf source JDepend pour la mise en forme if ( cycleName.equals( name ) ) { result += "|->" + name + "\n"; } else { result += "| " + name + "\n"; } } // retourne le cycle sous une forme affichable sur le web if ( result.length() >= MAX_LEN ) { result += "\n..."; } return result; } }