/**
* 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.squalecommon.enterpriselayer.facade.rule;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.squale.jraf.commons.exception.JrafDaoException;
import org.squale.jraf.provider.persistence.hibernate.SessionImpl;
import org.squale.jraf.spi.persistence.ISession;
import org.squale.squalecommon.daolayer.component.AbstractComponentDAOImpl;
import org.squale.squalecommon.daolayer.component.AuditDAOImpl;
import org.squale.squalecommon.daolayer.component.AuditDisplayConfDAOImpl;
import org.squale.squalecommon.daolayer.component.AuditGridDAOImpl;
import org.squale.squalecommon.daolayer.config.AdminParamsDAOImpl;
import org.squale.squalecommon.daolayer.result.MarkDAOImpl;
import org.squale.squalecommon.daolayer.result.MeasureDAOImpl;
import org.squale.squalecommon.daolayer.result.QualityResultDAOImpl;
import org.squale.squalecommon.enterpriselayer.businessobject.component.AbstractComponentBO;
import org.squale.squalecommon.enterpriselayer.businessobject.component.AuditBO;
import org.squale.squalecommon.enterpriselayer.businessobject.component.AuditDisplayConfBO;
import org.squale.squalecommon.enterpriselayer.businessobject.component.AuditGridBO;
import org.squale.squalecommon.enterpriselayer.businessobject.component.ProjectBO;
import org.squale.squalecommon.enterpriselayer.businessobject.config.AdminParamsBO;
import org.squale.squalecommon.enterpriselayer.businessobject.config.Profile_DisplayConfBO;
import org.squale.squalecommon.enterpriselayer.businessobject.result.CriteriumResultBO;
import org.squale.squalecommon.enterpriselayer.businessobject.result.FactorResultBO;
import org.squale.squalecommon.enterpriselayer.businessobject.result.MarkBO;
import org.squale.squalecommon.enterpriselayer.businessobject.result.MeasureBO;
import org.squale.squalecommon.enterpriselayer.businessobject.result.PracticeResultBO;
import org.squale.squalecommon.enterpriselayer.businessobject.result.QualityResultCommentBO;
import org.squale.squalecommon.enterpriselayer.businessobject.rule.AbstractFormulaBO;
import org.squale.squalecommon.enterpriselayer.businessobject.rule.CriteriumRuleBO;
import org.squale.squalecommon.enterpriselayer.businessobject.rule.FactorRuleBO;
import org.squale.squalecommon.enterpriselayer.businessobject.rule.PracticeRuleBO;
import org.squale.squalecommon.util.manualmark.TimeLimitationParser;
import org.squale.squalecommon.util.mapping.Mapping;
/**
* Calcul d'un audit Le calcul d'un audit s'appuie sur des r�gles d�crites dans la grille qualit�, celle-ci est
* rattach�e � un projet. Les r�gles sont exprim�es en fonction de m�triques obtenues par des outils comme mccabe par
* exemple.
*/
public final class AuditComputing
{
/** Log */
private static Log LOG = LogFactory.getLog( AuditComputing.class );
/**
* Define whether or not we need at least two values to compute criterias or factors.
* 1 if we need two values.
* 0 if one is enough
*/
private static int MIN_NUMBER_OF_PRACTICES_OR_CRITERIAS = 1;
/**
* Define private constructor for utility class
*/
private AuditComputing()
{
}
/**
* Calcul d'un audit Le calcul d'un audit se fait � partir des pratiques pour remonter vers les facteurs
*
* @param pSession session
* @param pProject projet
* @param pAudit audit
* @return true If the last mark for a manual practice is out of date
* @throws JrafDaoException si erreur
*/
public static boolean computeAuditResult( ISession pSession, ProjectBO pProject, AuditBO pAudit )
throws JrafDaoException
{
boolean warning=false;
try
{
LOG.info( RuleMessages.getString( "computation.start" ) );
// D�composition de la grille en facteurs, criteres, pratiques
Map factors = new Hashtable();
Map criteria = new Hashtable();
Map practices = new Hashtable();
// Les maps contiennent en clef la r�gle et en valeur le r�sultat calcul�
flattenQualitfyGrid( pSession, pProject, pAudit, factors, criteria, practices );
// Le traitement se fait dans l'ordre hi�rarchique inverse
// cel� permet d'�viter les calculs multiples pour des facteurs
// ou des crit�res plusieurs fois pr�sents
// first : do we need at least two practices or two criterias to compute criterias or factors
List adminParamsList = AdminParamsDAOImpl.getInstance().findByKey( pSession, AdminParamsBO.TWO_TO_COMPUTE );
//we check if the parameter TWO_TO_COMPUTE exists
if ( adminParamsList.size() > 0 )
{
AdminParamsBO paramsBO = (AdminParamsBO) adminParamsList.get( 0 );
//we check the value and change the min number of criterias / practices if necessary
if ( paramsBO.getParamValue().equals( "false" ) )
{
MIN_NUMBER_OF_PRACTICES_OR_CRITERIAS = 0;
}
else
{
MIN_NUMBER_OF_PRACTICES_OR_CRITERIAS = 1;
}
}
// Traitement des pratiques
warning = computePracticesResults( pSession, pProject, pAudit, practices );
// Traitement des criteres
computeCriteriaResults( pSession, criteria, practices );
// Traitement des facteurs
computeFactorsResults( pSession, factors, criteria, practices );
// Ajout de la grille qualit�
AuditGridBO auditGrid = new AuditGridBO();
auditGrid.setGrid( pProject.getQualityGrid() );
auditGrid.setProject( pProject );
auditGrid.setAudit( pAudit );
AuditGridDAOImpl.getInstance().create( pSession, auditGrid );
// Ajout � l'audit
pAudit.addAuditGrid( auditGrid );
// Ajout des configurations
Collection confs = pProject.getProfile().getProfileDisplayConfs();
for ( Iterator it = confs.iterator(); it.hasNext(); )
{
AuditDisplayConfBO auditConf = new AuditDisplayConfBO();
auditConf.setDisplayConf( ( (Profile_DisplayConfBO) it.next() ).getDisplayConf() );
auditConf.setProject( pProject );
auditConf.setAudit( pAudit );
AuditDisplayConfDAOImpl.getInstance().create( pSession, auditConf );
// Ajout � l'audit
pAudit.addAuditDisplayConf( auditConf );
}
AuditDAOImpl.getInstance().save( pSession, pAudit );
}
finally
{
LOG.info( RuleMessages.getString( "computation.end" ) );
}
return warning;
}
/**
* Mise � plat d'une grille qualit� La structure hi�rarchique de la grille qualit� est mise � plat sous la forme de
* 3 maps qui contiennent les r�gles en fonction de leur typologie
*
* @param pSession session
* @param pProject projet
* @param pAudit audit
* @param pFactors map des facteurs
* @param pCriteria map des crit�res
* @param pPractices map des pratiques
* @throws JrafDaoException si erreur
*/
private static void flattenQualitfyGrid( ISession pSession, ProjectBO pProject, AuditBO pAudit, Map pFactors,
Map pCriteria, Map pPractices )
throws JrafDaoException
{
// Le parcours consiste � construire 3 maps avec les facteurs, criteres et pratiques
// Ce partionnement permet le traitement plus efficace d'au audit dans le cas o� des crit�res
// ou des pratiques apparaissent de fa�on multiple
// On place aussi dans ces map, en tant que valeur le r�sultat associ�, par exemple
// la map des facteurs contient les FactorRuleBO et les FactorResultBO
Iterator factors = pProject.getQualityGrid().getFactors().iterator();
// Parcours des facteurs
while ( factors.hasNext() )
{
FactorRuleBO factor = (FactorRuleBO) factors.next();
// Cr�ation du r�sultat associ�
FactorResultBO fResult = new FactorResultBO();
fResult.setRule( factor );
fResult.setProject( pProject );
fResult.setAudit( pAudit );
pFactors.put( factor, fResult );
QualityResultDAOImpl.getInstance().create( pSession, fResult );
Iterator criteria = factor.getCriteria().keySet().iterator();
// Parcours des crit�res
while ( criteria.hasNext() )
{
CriteriumRuleBO criterium = (CriteriumRuleBO) criteria.next();
// Un crit�re peut �tre pr�sent dans plusieurs facteurs
// On ne le rajoute que si n�cessaire
if ( pCriteria.get( criterium ) == null )
{
// Cr�ation du r�sultat associ�
CriteriumResultBO cResult = new CriteriumResultBO();
cResult.setRule( criterium );
cResult.setProject( pProject );
cResult.setAudit( pAudit );
QualityResultDAOImpl.getInstance().create( pSession, cResult );
// Ajout dans la map
pCriteria.put( criterium, cResult );
}
Iterator practices = criterium.getPractices().keySet().iterator();
// Parcours des pratiques
while ( practices.hasNext() )
{
PracticeRuleBO practice = (PracticeRuleBO) practices.next();
// Une pratique peut �tre pr�sente dans plusieurs crit�res
// On ne le rajoute que si n�cessaire
if ( pPractices.get( practice ) == null )
{
// Cr�ation du r�sultat associ�
PracticeResultBO pResult = new PracticeResultBO();
pResult.setRule( practice );
pResult.setProject( pProject );
pResult.setAudit( pAudit );
QualityResultDAOImpl.getInstance().create( pSession, pResult );
// Ajout dans la map
pPractices.put( practice, pResult );
}
}
}
}
}
/**
* Calcul des facteurs Chaque facteur est calcul� en fonction des crit�res qui le composent
*
* @param pSession session
* @param pFactors facteurs
* @param pCriteria crit�res
* @param pPractices pratiques
* @throws JrafDaoException si erreur
*/
private static void computeFactorsResults( ISession pSession, Map pFactors, Map pCriteria, Map pPractices )
throws JrafDaoException
{
// On parcourt chaque facteur et on calcule son r�sultat
Iterator factors = pFactors.keySet().iterator();
while ( factors.hasNext() )
{
// R�cup�ration du facteur et du r�sultat
FactorRuleBO factor = (FactorRuleBO) factors.next();
LOG.info( RuleMessages.getString( "computation.factor", new Object[] { factor.getName() } ) );
FactorResultBO result = (FactorResultBO) pFactors.get( factor );
// Le calcul se fait par une moyenne simple sur les crit�res
// calcul�s si au moins 2 criteres ont �t� calcul�s
int nbCriteria = 0; // Nombre de criteres calcul�s
float sumOfCriteriaWeight = 0;
float criteriaSum = 0; // Note cumul�e
// Parcours des criteres du facteur
Iterator it = factor.getCriteria().keySet().iterator();
// Pour chaque crit�re, on r�cup�re les valeurs calcul�es
while ( it.hasNext() )
{
CriteriumResultBO criteriumResult = (CriteriumResultBO) pCriteria.get( it.next() );
// Nota : criteriumResult ne peut pas �tre null
if ( -1 != criteriumResult.getMeanMark() )
{
float criteriaWeight =
( (Float) factor.getCriteria().get( criteriumResult.getRule() ) ).floatValue();
criteriaSum += criteriumResult.getMeanMark() * criteriaWeight;
sumOfCriteriaWeight += criteriaWeight;
nbCriteria++;
}
}
// the value of MIN_NUMBER_OF_PRACTICES_OR_CRITERIAS depends on whether or not we
// need at least two values to compute factors. This param is set in squale-config file
if ( nbCriteria > MIN_NUMBER_OF_PRACTICES_OR_CRITERIAS )
{
// calcul de la note du facteur
result.setMeanMark( criteriaSum / sumOfCriteriaWeight );
QualityResultDAOImpl.getInstance().save( pSession, result );
}
else
{
LOG.debug( RuleMessages.getString( "nocomputation", new Object[] { factor.getName() } ) );
}
}
}
/**
* Calcul des crit�res Chaque crit�re est calcul� en fonction des r�sultats de ses pratiques
*
* @param pSession session
* @param pCriteria crit�res
* @param pPractices pratiques
* @throws JrafDaoException si erreur
*/
private static void computeCriteriaResults( ISession pSession, Map pCriteria, Map pPractices )
throws JrafDaoException
{
// On parcourt chaque crit�re et on calcule son r�sultat
Iterator criteria = pCriteria.keySet().iterator();
while ( criteria.hasNext() )
{
// R�cup�ration du crit�re et de son r�sultat
CriteriumRuleBO criterium = (CriteriumRuleBO) criteria.next();
LOG.info( RuleMessages.getString( "computation.criterium", new Object[] { criterium.getName() } ) );
CriteriumResultBO result = (CriteriumResultBO) pCriteria.get( criterium );
// La moyenne se fait par un moyenne simple sur les pratiques
// calcul�s si au moins deux pratiques ont �t� calcul�es
int nbPractices = 0;
float sumOfPracticesWeight = 0;
float practicesSum = 0; // Note cumul�e
// Parcours des pratiques du crit�re
Iterator it = criterium.getPractices().keySet().iterator();
// Pour chaque crit�re, on r�cup�re les valeurs calcul�es
while ( it.hasNext() )
{
PracticeResultBO praticeResult = (PracticeResultBO) pPractices.get( it.next() );
// Nota : criteriumResult ne peut pas �tre null
if ( -1 != praticeResult.getMeanMark() )
{
float practiceWeight =
( (Float) criterium.getPractices().get( praticeResult.getRule() ) ).floatValue();
practicesSum += praticeResult.getMeanMark() * practiceWeight;
sumOfPracticesWeight += practiceWeight;
nbPractices++;
}
}
// the value of MIN_NUMBER_OF_PRACTICES_OR_CRITERIAS depends on whether or not we
// need at least two values to compute criterias. This param is set in squale-config file
if ( nbPractices > MIN_NUMBER_OF_PRACTICES_OR_CRITERIAS )
{
// calcul de la note du facteur
result.setMeanMark( practicesSum / sumOfPracticesWeight );
QualityResultDAOImpl.getInstance().save( pSession, result );
}
else
{
LOG.debug( RuleMessages.getString( "nocomputation", new Object[] { criterium.getName() } ) );
}
}
}
/**
* Calcul des pratiques Chaque pratique est calcul�e, son r�sultat est �crit en base de donn�es
*
* @param pSession session
* @param pProject projet
* @param pAudit audit
* @param pPractices pratiques
* @return true If the last mark for a manual practice is out of date
* @throws JrafDaoException si erreur
*/
private static boolean computePracticesResults( ISession pSession, ProjectBO pProject, AuditBO pAudit, Map pPractices )
throws JrafDaoException
{
boolean warning = false;
// Les pratiques sont tri�es par par type
// de composant pour eviter les appels multiples de recuperation des
// composants du projet
Map kindPractices = splitPracticesByKind( pPractices );
// On traite s�parement les pratiques de niveau projet
computeProjectPractices( pSession, pProject, pAudit, (Collection) kindPractices.remove( "project" ), pPractices );
// Processing of the manual practices
warning=computeManualPractices( pSession, pProject, (Collection) kindPractices.remove( "" ), pPractices );
Iterator kinds = kindPractices.keySet().iterator();
while ( kinds.hasNext() )
{
String kind = (String) kinds.next();
LOG.info( RuleMessages.getString( "computation.kind", new Object[] { kind } ) );
// sauvegarde du mode actuel de flush
Session hibernateSession = ( (SessionImpl) pSession ).getSession();
FlushMode oldFlushMode = hibernateSession.getFlushMode();
// On ne traite pas les pratiques qui n'ont pas de composant associ�
// (elles n'ont pas de formule)
if ( kind.length() > 0 )
{
try
{
// flush de la session
hibernateSession.flush();
// le flush automatique est d�sactiv� pendant tout le calcul de chaque notes
hibernateSession.setFlushMode( FlushMode.NEVER );
// Recup�ration des enfants de bons niveaux...
Collection children =
AbstractComponentDAOImpl.getInstance().findProjectChildren(
pSession,
pProject,
pAudit,
Mapping.getComponentClass( "component."
+ kind ) );
Iterator practices = ( (Collection) kindPractices.get( kind ) ).iterator();
while ( practices.hasNext() )
{
PracticeRuleBO practice = (PracticeRuleBO) practices.next();
LOG.info( RuleMessages.getString( "computation.practice", new Object[] { practice.getName() } ) );
PracticeResultBO practiceResult = (PracticeResultBO) pPractices.get( practice );
// V�rification de la formule
FormulaInterpreter interpreter = new FormulaInterpreter();
try
{
// V�rification de la syntaxe de la formule
// un exception est lev�e si elle est incorrecte
interpreter.checkSyntax( practice.getFormula() );
// Calcul de notes pour chaque enfant
Collection marks =
computePracticeResults( pSession, pAudit, interpreter, practice, practiceResult,
children );
// Calcul de la note globale de la pratique
computePracticeMark( practice, practiceResult, marks );
}
catch ( FormulaException fe )
{
// Si une erreur se produit sur le calcul d'une formule
// on stoppe le calcul pour cette pratique
LOG.error( RuleMessages.getString( "formula.error", new Object[] { practice.getName() } ),
fe );
}
catch ( WeightFunctionException wfe )
{
// Si une erreur se produit sur le calcul d'une formule
// on stoppe le calcul pour cette pratique
LOG.error( RuleMessages.getString( "function.error", new Object[] {
practice.getWeightFunction(), practice.getName() } ), wfe );
}
// Sauvegarde des r�sultats de la pratique
QualityResultDAOImpl.getInstance().save( pSession, practiceResult );
}
}
catch ( HibernateException e )
{
// Cette erreur est li�e � la gestion du flushmode, dans ce cas on se contente de remonter
// une exception JRAF
throw new JrafDaoException( e );
}
finally
{
// Remise du mode de flush au mode pr�cedent
hibernateSession.setFlushMode( oldFlushMode );
}
}
}
return warning;
}
/**
* Traitement des pratiques de niveau projet Les pratiques calcul�es au niveau d'un projet sont trait�es de fa�on
* sp�cifique, la formule donne une note directement sans avoir � collecter des notes pour chaque composant (comme
* c'est le cas au niveau de classes ou des m�thodes)
*
* @param pSession session
* @param pProject projet
* @param pAudit audit
* @param pProjectPractices pratiques de niveau projet
* @param pPractices pratiques
* @throws JrafDaoException si erreur
*/
private static void computeProjectPractices( ISession pSession, ProjectBO pProject, AuditBO pAudit,
Collection pProjectPractices, Map pPractices )
throws JrafDaoException
{
if ( pProjectPractices != null )
{
try
{
Iterator practices = pProjectPractices.iterator();
while ( practices.hasNext() )
{
PracticeRuleBO practice = (PracticeRuleBO) practices.next();
LOG.info( RuleMessages.getString( "computation.practice", new Object[] { practice.getName() } ) );
PracticeResultBO practiceResult = (PracticeResultBO) pPractices.get( practice );
// V�rification de la formule
FormulaInterpreter interpreter = new FormulaInterpreter();
try
{
// V�rification de la syntaxe de la formule
// un exception est lev�e si elle est incorrecte
interpreter.checkSyntax( practice.getFormula() );
// Calcul de la note globale de la pratique
computeProjectPracticeMark( pSession, pProject, pAudit, interpreter, practice, practiceResult );
}
catch ( FormulaException e )
{
// Si une erreur se produit sur le calcul d'une formule
// on stoppe le calcul pour cette pratique
LOG.error( RuleMessages.getString( "formula.error", new Object[] { practice.getName() } ), e );
}
// Sauvegarde des r�sultats de la pratique
QualityResultDAOImpl.getInstance().save( pSession, practiceResult );
}
}
catch ( HibernateException e )
{
// Cette erreur est li�e � la gestion du flushmode, dans ce cas on se contente de remonter
// une exception JRAF
throw new JrafDaoException( e );
}
}
}
/**
* Calcul de la note sur un projet Le calcul de la note sur un projet se fait directement au moyen de la formule
* associ�e
*
* @param pSession session
* @param pProject projet
* @param pAudit audit
* @param pInterpreter interpr�te
* @param pPractice pratique
* @param pPracticeResult r�sultat
* @throws FormulaException si erreur
* @throws JrafDaoException si erreur
*/
private static void computeProjectPracticeMark( ISession pSession, ProjectBO pProject, AuditBO pAudit,
FormulaInterpreter pInterpreter, PracticeRuleBO pPractice,
PracticeResultBO pPracticeResult )
throws JrafDaoException, FormulaException
{
// R�cup�ration des types de mesure trait�s par la formule
AbstractFormulaBO formula = pPractice.getFormula();
String[] measureKinds = new String[formula.getMeasureKinds().size()];
formula.getMeasureKinds().toArray( measureKinds );
MeasureBO[] measures = new MeasureBO[formula.getMeasureKinds().size()];
Long projectId = new Long( pProject.getId() );
// R�cup�ration de chaque mesure
boolean measuresNotNull = true;
Long auditId = new Long( pAudit.getId() );
for ( int i = 0; i < measureKinds.length; i++ )
{
MeasureBO measure =
(MeasureBO) MeasureDAOImpl.getInstance().load(
pSession,
projectId,
auditId,
Mapping.getMeasureClass( measureKinds[i] + "."
+ formula.getComponentLevel() ) );
measures[i] = measure;
// Une mesure peut �tre absente, ce qui serait li� � un probl�me de cr�ation des mesures
// dans la phase qui pr�c�de le calcul des notes
if ( measure == null )
{
measuresNotNull = false;
// On indique le type de mesure qui n'est pas trouv� ainsi que le composant concern�
LOG.warn( RuleMessages.getString( "missingmeasure",
new Object[] { measureKinds[i], projectId, auditId } ) );
}
}
// On calcul la note seulement si toutes les mesures requises sont bien pr�sentes
if ( measuresNotNull )
{
// Calcul de la note
// L'exception dans le calcul est remont�e telle quelle
Number value = pInterpreter.evaluate( pPractice.getFormula(), measures );
// On place une note -1 si la formule n'a pu �tre calcul�e
if ( value != null )
{
pPracticeResult.setMeanMark( value.floatValue() );
}
}
}
/**
* Calcul de la note d'une pratique
*
* @param pPractice pratique
* @param pPracticeResult r�sultats de la pratique
* @param pMarks les notes des composants impliqu�s
* @throws WeightFunctionException si erreur
*/
private static void computePracticeMark( PracticeRuleBO pPractice, PracticeResultBO pPracticeResult,
Collection pMarks )
throws WeightFunctionException
{
/*
* Formule : meanMark = weigh^-1(1/N sum(weight(note(component)))) Cela revient � faire la moyenne des notes
* auxquelles on applique la fonction la fonction de pond�ration weight puis � appliquer la fonction inverse
*/
// Moyenne des notes : 1/N * sum(weight(note(component))
float num = 0;
float denum = 0;
float average = -1;
// Interpreteur python
WeightFunctionInterpreter interpreter = new WeightFunctionInterpreter();
for ( Iterator marksIt = pMarks.iterator(); marksIt.hasNext(); )
{
// note(component)
float mark = ( (MarkBO) marksIt.next() ).getValue();
// Si la note sur le composant a �t� calcul�
if ( mark != MarkBO.NOT_NOTED_VALUE )
{
// weigth(note(component))
float weightMark = interpreter.exec( pPractice.getWeightFunction(), mark );
num += weightMark;
denum++;
}
}
if ( denum != 0 )
{
// Moyenne
average = num / denum;
// weight^-1(average)
Float meanMark = inverse( interpreter, pPractice.getWeightFunction(), average );
pPracticeResult.setMeanMark( meanMark.floatValue() );
}
else
{
LOG.debug( RuleMessages.getString( "nocomputation", new Object[] { pPractice.getName() } ) );
}
}
/**
* Fait le calcul de la fonction inverse.
*
* @param pInterpreter l'interpr�teur pour une fonction de pond�ration
* @param pFunction la fonction de pond�ration
* @param pY : pFunction(x) = pY
* @return x tel que f(x) = pY
* @throws WeightFunctionException si erreur
*/
private static Float inverse( WeightFunctionInterpreter pInterpreter, String pFunction, float pY )
throws WeightFunctionException
{
// pr�cision de x : 10^-3
final double signi = 3;
final double pow = 10;
double epsilon = Math.pow( pow, -signi ) / 2;
// x est compris entre 0 et 3
final float minMark = 0;
final float maxMark = 3;
float x1 = minMark, x2 = ( minMark + maxMark ) / 2, x3 = maxMark;
float fX1 = pInterpreter.exec( pFunction, x1 ), fX2 = pInterpreter.exec( pFunction, x2 );
float fX3 = pInterpreter.exec( pFunction, x3 );
float old = x1;
// Tant que x n'est pas avec la pr�cision voulu
while ( Math.abs( x2 - old ) > epsilon )
{
old = x2;
// si y est entre fX1 et fX2
if ( ( fX1 <= pY && fX2 > pY ) || ( fX1 >= pY && fX2 < pY ) )
{
// On se recale entre x1 et x2
x3 = x2;
fX3 = fX2;
}
else
{
// On se recale entre x2 et x3
x1 = x2;
fX1 = fX2;
}
// nouvelle valeur cible par dichotomie
x2 = ( x1 + x3 ) / 2;
fX2 = pInterpreter.exec( pFunction, x2 );
}
// Arrondie au nombre de chiffres significatifs
final double round = 0.5;
double num = ( x2 * Math.pow( pow, signi ) + round );
return new Float( (double) ( (int) ( x2 * Math.pow( pow, signi ) + round ) ) / Math.pow( pow, signi ) );
}
/**
* S�paration des pratiques par type de composant
*
* @param pPractices pratiques
* @return map de la forme (string,collection) o� string donne le type de composant et collection la liste des
* pratiques sur ce type de composant (string peut �tre "")
*/
private static Map splitPracticesByKind( Map pPractices )
{
Hashtable result = new Hashtable();
Iterator practices = pPractices.keySet().iterator();
while ( practices.hasNext() )
{
PracticeRuleBO practice = (PracticeRuleBO) practices.next();
// Les pratiques sans formule sont regroup�es
// avec un kind vide
String kind = "";
if ( practice.getFormula() != null )
{
kind = practice.getFormula().getComponentLevel();
}
Collection practicesKind = (Collection) result.get( kind );
if ( practicesKind == null )
{
practicesKind = new ArrayList();
result.put( kind, practicesKind );
}
practicesKind.add( practice );
}
return result;
}
/**
* Calcul des pratiques sur les composants
*
* @param pSession session
* @param pAudit audit
* @param pInterpreter interpr�teur
* @param pPractice pratique
* @param pPracticeResult r�sultat
* @param pChildren composants fils
* @return les notes
* @throws FormulaException si erreur
* @throws JrafDaoException si erreur
*/
private static Collection computePracticeResults( ISession pSession, AuditBO pAudit,
FormulaInterpreter pInterpreter, PracticeRuleBO pPractice,
PracticeResultBO pPracticeResult, Collection pChildren )
throws FormulaException, JrafDaoException
{
// Reinitialisation des r�partitions
pPracticeResult.resetRepartitions();
// R�cup�ration des types de mesure trait�s par la formule
AbstractFormulaBO formula = pPractice.getFormula();
String[] measureKinds = new String[formula.getMeasureKinds().size()];
formula.getMeasureKinds().toArray( measureKinds );
MeasureBO[] measures = new MeasureBO[formula.getMeasureKinds().size()];
Long componentId = null;
// Traitement de la note pour chaque composant
Iterator it = pChildren.iterator();
// On stocke les notes
ArrayList marks = new ArrayList();
while ( it.hasNext() )
{
AbstractComponentBO child = (AbstractComponentBO) it.next();
componentId = new Long( child.getId() );
// R�cup�ration de chaque mesure
boolean measuresNotNull = true;
for ( int i = 0; i < measureKinds.length; i++ )
{
Long auditId = new Long( pAudit.getId() );
MeasureBO measure =
(MeasureBO) MeasureDAOImpl.getInstance().load(
pSession,
componentId,
auditId,
Mapping.getMeasureClass( measureKinds[i] + "."
+ formula.getComponentLevel() ) );
measures[i] = measure;
// Une mesure peut �tre absente, ce qui serait li� � un probl�me de cr�ation des mesures
// dans la phase qui pr�c�de le calcul des notes
if ( measure == null )
{
measuresNotNull = false;
// On indique le type de mesure qui n'est pas trouv� ainsi que le composant concern�
LOG.debug( RuleMessages.getString( "missingmeasure", new Object[] { measureKinds[i], componentId,
auditId } ) );
}
}
// On calcul la note seulement si toutes les mesures requises sont bien pr�sentes
if ( measuresNotNull )
{
MarkBO mark = computeMark( pSession, pInterpreter, measures, pPractice, pPracticeResult, child );
marks.add( mark );
}
}
return marks;
}
/**
* Calcul d'une note sur un composant
*
* @param pSession session
* @param pInterpreter interpr�te
* @param pMeasures mesures
* @param pPractice pratique
* @param pPracticeResult r�sultat de pratique
* @param pChild composant
* @return la note
* @throws FormulaException si erreur
* @throws JrafDaoException si erreur
*/
private static MarkBO computeMark( ISession pSession, FormulaInterpreter pInterpreter, MeasureBO[] pMeasures,
PracticeRuleBO pPractice, PracticeResultBO pPracticeResult,
AbstractComponentBO pChild )
throws FormulaException, JrafDaoException
{
MarkBO mark = new MarkBO();
mark.setComponent( pChild );
mark.setPractice( pPracticeResult );
// Calcul de la note
// L'exception dans le calcul est remont�e telle quelle
Number value = pInterpreter.evaluate( pPractice.getFormula(), pMeasures );
// On place une note -1 si la formule n'a pu �tre calcul�e
if ( value == null )
{
mark.setValue( MarkBO.NOT_NOTED_VALUE );
}
else
{
mark.setValue( value.floatValue() );
}
// Mise � jour de la r�partition des notes
pPracticeResult.incrementRepartition( mark.getValue() );
// Enregistrement de la note
MarkDAOImpl.getInstance().save( pSession, mark );
return mark;
}
/**
* This method push into the base, for the current audit, the manual mark which are filled and still valid
*
* @param session Hibernate session
* @param project The project
* @param manualPractices collection of practice without level (manual practice)
* @param practicesMap map of all the practice.
* @return true If the last mark for a manual practice is out of date
* @throws JrafDaoException Exception happened during hibernate work
*/
private static boolean computeManualPractices( ISession session, ProjectBO project, Collection manualPractices,
Map practicesMap )
throws JrafDaoException
{
boolean warning = false;
if ( manualPractices != null )
{
try
{
QualityResultDAOImpl dao = QualityResultDAOImpl.getInstance();
// For each manual practice we try to set a mark for the audit
Iterator<PracticeRuleBO> practicesIt = manualPractices.iterator();
while ( practicesIt.hasNext() )
{
PracticeRuleBO practice = practicesIt.next();
LOG.info( RuleMessages.getString( "computation.practice", new Object[] { practice.getName() } ) );
// search of the last manual mark inserted
PracticeResultBO manualPraticeResult =
dao.findLastManualMark( session, project.getId(), practice.getId() );
// If there is one manual mark inserted
if ( manualPraticeResult != null )
{
// recovering of the PracticeResultBO which will recorded
PracticeResultBO practiceResult = (PracticeResultBO) practicesMap.get( practice );
/*
* If the current date is before the date of creation of the manual mark plus the period of
* validity then we put the mark for the current audit.
*/
if ( isMarkValid( practice, manualPraticeResult ) )
{
practiceResult.setMeanMark( manualPraticeResult.getMeanMark() );
practiceResult.setCreationDate( manualPraticeResult.getCreationDate() );
//We add the comments if it exists
if( manualPraticeResult.getManualMarkComment() != null )
{
QualityResultCommentBO practiceResultComments = new QualityResultCommentBO();
practiceResultComments.setQualityResult( practiceResult );
practiceResultComments.setComments( manualPraticeResult.getManualMarkComment().getComments() );
practiceResult.setManualMarkComment( practiceResultComments );
}
dao.save( session, practiceResult );
}
else
{
warning=true;
}
}
}
}
catch ( HibernateException e )
{
throw new JrafDaoException( e );
}
}
return warning;
}
/**
* This method determine whether the mark is valid (according to the period validity)
*
* @param practice The practice for the current audit
* @param manualPraticeResult The last manualPratice inserted
* @return true if the the mark is valid
*/
private static boolean isMarkValid( PracticeRuleBO practice, PracticeResultBO manualPraticeResult )
{
boolean valid = true;
// Date of creation of the manual mark
Date creationDate = manualPraticeResult.getCreationDate();
// The validity period
String validityPeriod = practice.getTimeLimitation();
// Test the validity of the mark
valid = TimeLimitationParser.isMarkValid( validityPeriod, creationDate );
return valid;
}
}