/**
* 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.Hashtable;
import java.util.Iterator;
import java.util.Map;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.squale.jraf.commons.exception.JrafDaoException;
import org.squale.jraf.helper.LoggingHelper;
import org.squale.jraf.provider.persistence.hibernate.SessionImpl;
import org.squale.jraf.spi.logging.ILogger;
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.AuditGridDAOImpl;
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.AuditGridBO;
import org.squale.squalecommon.enterpriselayer.businessobject.component.ProjectBO;
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.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.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 class AuditComputing {
/** Log */
private static ILogger LOG = LoggingHelper.getInstance(AuditComputing.class);
/**
* 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
* @throws JrafDaoException si erreur
*/
public static void computeAuditResult(ISession pSession, ProjectBO pProject, AuditBO pAudit) throws JrafDaoException {
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
// Traitement des pratiques
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);
AuditDAOImpl.getInstance().save(pSession, pAudit);
} finally {
LOG.info(RuleMessages.getString("computation.end"));
}
}
/**
* 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().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().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 criteriaSum = 0; // Note cumul�e
// Parcours des criteres du facteur
Iterator it = factor.getCriteria().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()) {
criteriaSum += criteriumResult.getMeanMark();
nbCriteria++;
}
}
// Calcul seulement s'il existe au moins deux r�sultats
if (nbCriteria > 1) {
// calcul de la note du facteur
result.setMeanMark(criteriaSum / nbCriteria);
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; // Nombre de criteres calcul�s
float practicesSum = 0; // Note cumul�e
// Parcours des pratiques du crit�re
Iterator it = criterium.getPractices().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()) {
practicesSum += praticeResult.getMeanMark();
nbPractices++;
}
}
// Calcul seulement s'il existe au moins deux r�sultats
if (nbPractices > 1) {
// calcul de la note du facteur
result.setMeanMark(practicesSum / nbPractices);
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
* @throws JrafDaoException si erreur
*/
private static void computePracticesResults(ISession pSession, ProjectBO pProject, AuditBO pAudit, Map pPractices) throws JrafDaoException {
// 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);
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
computePracticeResults(pSession, pAudit, interpreter, practice, practiceResult, children);
// Calcul de la note globale de la pratique
computePracticeMark(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);
} finally {
// Remise du mode de flush au mode pr�cedent
hibernateSession.setFlushMode(oldFlushMode);
}
}
}
}
/**
* 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
*/
private static void computePracticeMark(PracticeRuleBO pPractice, PracticeResultBO pPracticeResult) {
/*
* Formule :
* 0*nb0*pond0 + 1*nb1*pond1 + 2*nb2*pond2 + 3*nb3*pond3
* meanMark = -------------------------------------------------------
* nb0*pond0 + nb1*pond1 + nb2*pond2 + nb3*pond3
*/
float num = 0;
float denum = 0;
Iterator coefs = pPractice.getCoefficients().iterator();
int i = 0;
while (coefs.hasNext()) {
Float coef = (Float) coefs.next();
num += i * pPracticeResult.getRepartition()[i].intValue() * coef.floatValue();
denum += pPracticeResult.getRepartition()[i].intValue() * coef.floatValue();
i++;
}
if (denum != 0) {
pPracticeResult.setMeanMark(num / denum);
} else {
LOG.debug(RuleMessages.getString("nocomputation", new Object[] { pPractice.getName()}));
}
}
/**
* 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
* @throws FormulaException si erreur
* @throws JrafDaoException si erreur
*/
private static void 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();
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.warn(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) {
computeMark(pSession, pInterpreter, measures, pPractice, pPracticeResult, child);
}
}
}
/**
* 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
* @throws FormulaException si erreur
* @throws JrafDaoException si erreur
*/
private static void 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(-1);
} else {
mark.setValue(value.intValue());
}
// Mise � jour de la r�partition des notes
if (mark.getValue() >= 0) {
pPracticeResult.incrementRepartition(mark.getValue());
}
// Enregistrement de la note
MarkDAOImpl.getInstance().save(pSession, mark);
}
}