Java tutorial
/** * 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.rsm; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.squale.jraf.commons.exception.JrafDaoException; import org.squale.jraf.spi.persistence.ISession; import org.squale.squalecommon.daolayer.result.MeasureDAOImpl; import org.squale.squalecommon.enterpriselayer.businessobject.component.AuditBO; import org.squale.squalecommon.enterpriselayer.businessobject.component.ProjectBO; import org.squale.squalecommon.enterpriselayer.businessobject.result.rsm.RSMClassMetricsBO; import org.squale.squalecommon.enterpriselayer.businessobject.result.rsm.RSMMethodMetricsBO; import org.squale.squalecommon.enterpriselayer.businessobject.result.rsm.RSMProjectMetricsBO; import org.squale.squalix.core.TaskData; import org.squale.squalix.util.csv.CSVParser; import org.squale.squalix.util.parser.LanguageParser; import org.squale.squalix.util.repository.ComponentRepository; /** * Objet charg de faire persister les rsultats RSM */ public class RSMPersistor { /** Le template pour le rapport de classe */ private String mClassTemplate = "csv.template.class"; /** Le template pour le rapport de classe */ private String mMethodTemplate = "csv.template.method"; /** * Nombre de mthodes */ private int mNumberOfMethods = 0; /** * Nombre de classes */ private int mNumberOfClasses = 0; /** * Nombre de lignes de commentaires sur le projet */ private int mComments = 0; /** * Nombre de lignes de commentaires sur le projet */ private int mSLOC = 0; /** le nom de la tache rel, pour diffrencier java et cpp */ private String mTaskName; /** * Configuration */ private RSMConfiguration mConfiguration; /** * Chemin du fichier parser */ private String mReportFileName = null; /** * Logger */ private static final Log LOGGER = LogFactory.getLog(RSMPersistor.class); /** * Audit durant lequel l'analyse est effectue */ private AuditBO mAudit = null; /** * Projet sur lequel est ralise l'analyse. */ private ProjectBO mProject = null; /** * Session Persistance */ private ISession mSession = null; /** les paramtres temporaires */ private TaskData mDatas; /** le parser CSV */ private CSVParser mParser; /** l'adaptateur permettant de relier les rsultats RSM au composants associs */ private RSMAdaptator mAdaptator; /** pour faciliter le stockage */ private ComponentRepository mRepository; /** Le marqueur pour prprocesser le rapport gnr et en extraire les mtriques de classes */ private final static String CLASS_LOCATOR = "Class,"; /** Le marqueur pour prprocesser le rapport gnr et en extraire les mtriques de mthodes */ private final static String METHOD_LOCATOR = "Function,"; /** Marqueur pour signaler la fin de la zone doublon */ private final static String FILE_LOCATOR = "File,"; /** Marqueur pour signaler le dbut de la zone doublon */ private final static String STOP_WRITE = "ProjectFunctionMetrics"; /** Marqueur pour signaler la fin de la zone doublon */ private final static String RESTART_WRITE = "ProjectClassMetrics"; /** * Chane contenant l'expression rgulire permettant de effacer les paramtres 3 5 de la ligne mthode */ private String mREGEXPREMOVEPARAMS = ",[^,]*\\([^,]*\\),[^,]*,[^,]*"; /** * Chane contenant l'expression rgulire permettant de effacer les paramtres function point attach aux champs LOC, ELOC, LLOC */ private String mREGEXPREMOVEFUNCTIONPOINT = ",([^,]*)/[^,]*"; /** * Constructeur. * * @param pConfiguration configuration du framework. * @param pAudit audit encadrant l'excution. * @param pSession la session de persistance utilise par la tche. * @param pDatas la liste des paramtres temporaires du projet * @param pTaskName le nom de la tache (pour diffrencier java et cpp) * @param pLanguageParser le parser de language pour persister les donnes * @throws JrafDaoException si une session de peristance ne peut tre cre. */ public RSMPersistor(final RSMConfiguration pConfiguration, final AuditBO pAudit, final ISession pSession, TaskData pDatas, String pTaskName, LanguageParser pLanguageParser) throws JrafDaoException { mSession = pSession; mConfiguration = pConfiguration; mAudit = pAudit; mDatas = pDatas; mProject = pConfiguration.getProject(); mRepository = new ComponentRepository(mProject, mSession); mTaskName = pTaskName; mAdaptator = new RSMAdaptator(pLanguageParser, mRepository); } /** * Parse le rapport pour obtenir tous les mtriques * * @param pFileName chemin du fichier rapport. * @param pDatas les donnes * @throws Exception si un problme de parsing apparait. * @return le nombre de mtriques */ public int parseReport(final String pFileName, TaskData pDatas) throws Exception { mReportFileName = pFileName; LOGGER.info(RSMMessages.getString("logs.debug.report_parsing_class") + mReportFileName); mParser = new CSVParser(RSMMessages.getString("csv.config.file")); // effecue les diffrents prprocessing managePreProcess(pFileName); int nbClassResults = parseReportForClassMetrics(mConfiguration.getClassReportPath()); int nbMethodsResults = parseMethodReport(mConfiguration.getMethodsReportPath(), pDatas); return nbClassResults + nbMethodsResults; } /** * Effectur les diffrents prprocess ncessaires * * @param pFileName le nom du fichier gnr par RSM * @throws IOException en cas de problemes lors du pr-processing */ private void managePreProcess(String pFileName) throws IOException { // On effectue un prprocessing du fichier rsultat car le format CSV renvoy n'est pas vraiment du CSV, // Il y a des lignes supplmentaires et des informations gnantes pour le parsing eraseDouble(pFileName, mConfiguration.getAuxFile()); preProcess(mConfiguration.getClassReportPath(), mConfiguration.getAuxFile(), CLASS_LOCATOR); preProcess(mConfiguration.getMethodsReportPath(), mConfiguration.getAuxFile(), METHOD_LOCATOR); } /** * @param pCSVOutputFile nom du fichier pars pour le mettre au format CSV correct * @param pCSVOutputFileAux nom du fichier auxilliaire utilis pour supprimer les doublons * @param pMarker la chaine repre * @throws IOException en cas de problemes avec le prprocessing du fichier */ private void preProcess(String pCSVOutputFile, String pCSVOutputFileAux, String pMarker) throws IOException { // Ecrit a partir du deuxime fichier File f = new File(pCSVOutputFile); BufferedWriter bw = new BufferedWriter(new FileWriter(pCSVOutputFile)); BufferedReader br = new BufferedReader(new FileReader(pCSVOutputFileAux)); String line = ""; List results = new ArrayList(0); while (line != null) { line = br.readLine(); // Null signifie fin de fichier if (line != null) { // Si on a trouv une ligne commencant par le repre pass en paramtre, on la rcupre // Pour plus de suret on vrifie galement que la ligne contient une virgule // Traitement diffrent suivant les classes ou les fichiers, pour les classes on ne s'occupe // pas du nom du fichier, pour les mthodes oui if (pMarker.equals(CLASS_LOCATOR)) { if (isValidCSVLine(line, pMarker)) { bw.write(line + "\n"); } } else { if (isValidCSVLine(line, pMarker)) { results.add(line); } else if (isFileLine(line)) { // On est sur la ligne indiquant le fichier dans lequel se trouve tous les lments // obtenus aux lignes prcdentes // on crit les rsultats prcdents avec le nom du fichier en plus StringTokenizer st = new StringTokenizer(line, ","); String fileName = ""; int counter = 0; while (st.hasMoreElements() && counter < 2) { fileName = st.nextToken().trim(); counter++; } // on crit la ligne for (int i = 0; i < results.size(); i++) { // efface le retour la ligne String result = ((String) (results.get(i))).replaceAll("\n", ""); // efface paramtres 3 5 et function points result = result.replaceFirst(mREGEXPREMOVEPARAMS, "") .replaceAll(mREGEXPREMOVEFUNCTIONPOINT, ",$1"); // Dans ce cas il faut rajouter un " " sinon le parser ne tient pas compte de la colonne if (result.charAt(result.length() - 1) != ' ') { bw.write(result + " ," + fileName + "\n"); } else { bw.write(result + "," + fileName + "\n"); } } // reset la liste des rsultats results = new ArrayList(0); } } } } // ferme les buffers bw.close(); br.close(); } /** * @param pLine la ligne courante * @return true si la ligne commence par le marqueur de ligne fichier */ private boolean isFileLine(String pLine) { return pLine.startsWith(FILE_LOCATOR); } /** * @param pFilename le nom du fichier gnr par rsm * @param pOutputFileAux nom du fichier auxilliaire utilis pour supprimer les doublons * @throws IOException en cas d'chec de lecture */ private void eraseDouble(String pFilename, String pOutputFileAux) throws IOException { BufferedWriter bw = new BufferedWriter(new FileWriter(pOutputFileAux)); BufferedReader br = new BufferedReader(new FileReader(pFilename)); String line = ""; // Supprime les lignes redondantes du l'utilisation (ncessaire) // de deux options donnant en partie le meme rsultat boolean write = true; while (line != null) { line = br.readLine(); // Null signifie fin de fichier if (line != null) { // on enlve tous les espaces (il n'est pas cens y avoir d'espaces dans un fichier CSV) // et dans le fichier renvoy par RSM il y en a un peu partout // Toutefois on enlve pas l'espace qui est entre deux virgules sinon le parser ne compte pas la colonne String properLine = ""; for (int i = 0; i < line.length(); i++) { if (line.charAt(i) != ' ' || (line.charAt(i) == ' ' && i > 0 && i < (line.length() - 1) && line.charAt(i - 1) == ',' && line.charAt(i + 1) == ',')) { properLine += line.charAt(i); } } // Si on a trouv une ligne commencant par le repre pass en paramtre, on la rcupre // Pour plus de suret on vrifie galement que la ligne contient une virgule if (properLine.startsWith(STOP_WRITE)) { write = false; } else { if (properLine.startsWith(RESTART_WRITE)) { write = true; } } if (write && isValidCSVLine(properLine)) { bw.write(properLine + "\n"); } } } // ferme les buffers bw.close(); br.close(); } /** * Effectue un test plus gnral sans marqueur Sert pour l'tape 1 du pr-processing * * @param pLine la ligne tester * @return true si la ligne est valide pour prprocessing CSV */ private boolean isValidCSVLine(String pLine) { // On vrifie que la ligne comporte les lments ncessaire l'analyse CSV // Il faut une virgule return commonValidCVSLine(pLine) && (pLine.startsWith(CLASS_LOCATOR) || pLine.startsWith(FILE_LOCATOR) || pLine.startsWith(METHOD_LOCATOR)); } /** * Effectue les tests de prprocessing pour rcuprer les donnes qui interressent en fonction du marqueur Sert pour * l'tape 2 du pr-processing * * @param pLine la ligne tester * @param pMarker le repre * @return true si la ligne est valide pour analyse CSV */ private boolean isValidCSVLine(String pLine, String pMarker) { // On vrifie que la ligne comporte les lments ncessaire l'analyse CSV // Il faut une virgule return commonValidCVSLine(pLine) // Les contraintes sur la taille && pLine.length() > pMarker.length() && pMarker.equals(pLine.substring(0, pMarker.length())); } /** * Permet de traiter les conditions communes qu'une ligne doit avoir pour tre analyse par CSV * * @param pLine la ligne vrifier * @return true si la ligne est partiellement valide pour prprocessing CSV */ private boolean commonValidCVSLine(String pLine) { // On vrifie que la ligne comporte les lments ncessaire l'analyse CSV // Il faut une virgule return pLine.indexOf(",") != -1 // Les mots cls interdits && pLine.indexOf("Average") == -1 && pLine.indexOf("Total") == -1 && pLine.indexOf("Maximum") == -1 && pLine.indexOf("Minimum") == -1; } /** * Parse le rapport pour obtenir les mtriques de classe. * * @param pFilename chemin du fichier rapport. * @throws Exception si un problme de parsing apparait. * @return le nombre de rsultats de niveau classe * @roseuid 42B976100269 */ private int parseReportForClassMetrics(final String pFilename) throws Exception { // Rcuprer les beans issus du rapport de classes RSM Collection classResults = mParser.parse(RSMMessages.getString(mClassTemplate), pFilename); // On ne compte que les classes qui ont une logique Collection classToPersist = new ArrayList(0); // Ajout de la volumtrie dans les beans Iterator it = classResults.iterator(); RSMClassMetricsBO bo = null; while (it.hasNext()) { // On adapte chaque bean issu du rapport bo = (RSMClassMetricsBO) it.next(); // On ne compte que les classes qui ont une logique, c'est dire les classes ayant des mthodes if ((bo.getPublicData().intValue() + bo.getProtectedData().intValue() + bo.getPrivateData().intValue()) > 0) { // rattache le bo rsm la classe associe mAdaptator.adaptClassResult(bo); // on ajoute pour les donnes projets les donnes concernant la classe courante // au niveau des commentaires et du nombre de lignes de code mSLOC += bo.getSloc().intValue(); mComments += bo.getComments().intValue(); bo.setAudit(mAudit); mNumberOfClasses++; bo.setTaskName(mTaskName); classToPersist.add(bo); } } LOGGER.info(RSMMessages.getString("logs.debug.report_parsing_database")); // On sauvegarde le mesures sur les classes MeasureDAOImpl.getInstance().saveAll(mSession, classToPersist); mSession.commitTransactionWithoutClose(); mSession.beginTransaction(); LOGGER.info(RSMMessages.getString("logs.debug.report_parsing_end")); return classResults.size(); } /** * Parse le rapport pour obtenir les mtriques de mthodes * * @param pFilename chemin du fichier rapport. * @param pDatas les donnes temporaires * @throws Exception si un problme de parsing apparat. * @return le nombre de rsultats de niveau mthode rcuprs. * @roseuid 42B9761F015F */ private int parseMethodReport(final String pFilename, TaskData pDatas) throws Exception { // Rcuprer les beans issus du rapport de mthodes RSM Collection methodResults = mParser.parse(RSMMessages.getString(mMethodTemplate), pFilename); RSMMethodMetricsBO bo = null; String name = null; // On cre une nouvelle collection contenant les beans faire effectivement persister List resultsToPersist = new ArrayList(); // Adaptation des beans Iterator it = methodResults.iterator(); while (it.hasNext()) { bo = (RSMMethodMetricsBO) it.next(); // rattache le bo rsm la classe associe bo.setAudit(mAudit); // Le nom du fichier est mis en relatif par rapport la racine du projet String completFileName = bo.getFileName(); if (completFileName.indexOf("vobs") != -1) { String fileName = completFileName.substring(completFileName.indexOf("vobs"), completFileName.length()); bo.setFileName(fileName); } else { bo.setFileName(completFileName); } bo.setTaskName(mTaskName); mNumberOfMethods++; // Problme RSM avec le polymorphisme, si plusieurs mthodes de meme nom prsentes // dans le fichier on ne les sauvegarde pas boolean canAdd = mAdaptator.adaptMethodResult(bo); if (canAdd) { resultsToPersist.add(bo); } } // La collection des rsultats de mthodes est persiste LOGGER.info(RSMMessages.getString("logs.debug.report_parsing_database")); MeasureDAOImpl.getInstance().saveAll(mSession, resultsToPersist); mSession.commitTransactionWithoutClose(); mSession.beginTransaction(); LOGGER.info(RSMMessages.getString("logs.debug.report_parsing_end")); return methodResults.size(); } /** * Cre et fait persister les rsultats de niveau projet. * * @roseuid 42E09E5201AB */ public void persistProjectResult() { LOGGER.info(RSMMessages.getString("logs.debug.project_database")); RSMProjectMetricsBO metrics = new RSMProjectMetricsBO(); // Cration des mtriques de niveau projet metrics.setComponent(mProject); metrics.setAudit(mAudit); metrics.setTaskName(mTaskName); metrics.setNumberOfClasses(new Integer(mNumberOfClasses)); metrics.setNumberOfMethods(new Integer(mNumberOfMethods)); metrics.setSloc(new Integer(mSLOC)); metrics.setComments(new Integer(mComments)); try { MeasureDAOImpl.getInstance().create(mSession, metrics); } catch (JrafDaoException e) { LOGGER.error(e, e); } } }