/** * 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.util.parser; import java.util.TreeSet; import org.squale.squalecommon.enterpriselayer.businessobject.component.AbstractComplexComponentBO; import org.squale.squalecommon.enterpriselayer.businessobject.component.ClassBO; import org.squale.squalecommon.enterpriselayer.businessobject.component.MethodBO; import org.squale.squalecommon.enterpriselayer.businessobject.component.PackageBO; import org.squale.squalecommon.enterpriselayer.businessobject.component.ProjectBO; /** * Parse les noms de type C++ et les remplace par les objets correspondants. */ public class CppParser implements LanguageParser { /** Nom du package par d�faut */ private static final String DEFAULT_PACKAGE_NAME = "{C}"; /** Nom de la classe par d�faut */ private static final String DEFAULT_CLASS_NAME = "{C}"; /** * Pattern pour rep�rer une zone d'initialisation statique */ private static final String STATIC_INITIALIZER_PATTERN = "^.*#StaticInitializer#[0-9]*$"; /** Projet sur lequel est r�alis� l'analyse */ private ProjectBO mProject; /** * Ensemble des classes connues Cel� permet de renseigner les noms de classe connues pour permettre une distinction * entre un namespace et un nom de classe. McCabe utilise l'op�rateur de qualification :: aussi bien pour une classe * imbriqu�e que pour un namespace */ private TreeSet mKnownClasses = new TreeSet(); /** * Constructeur * * @param pProject le projet sur lequel est r�alis� l'analyse */ public CppParser( ProjectBO pProject ) { mProject = pProject; } /** * Ajout d'une classe connue * * @param pClassName nom de la classe */ public void addKnownClass( String pClassName ) { StringBuffer context = new StringBuffer(); String className = geRelativeElementName( pClassName, context ); mKnownClasses.add( className ); } /* ################ D�composition et transformation en objet correspondant ################ */ /** * {@inheritDoc} * * @see org.squale.squalix.util.parser.LanguageParser#getMethod(java.lang.String, java.lang.String) */ public MethodBO getMethod( String pAbsoluteMethodName, String pFileName ) { // On r�cup�re le nom de la m�thode MethodBO methodBO; try { String methodName = getMethodName( pAbsoluteMethodName ); methodBO = new MethodBO( methodName ); String nameWithoutParameters; // cas des m�thodes 'main' C++ inscrite sans signature par McCabe if ( methodName.equals( "main" ) || methodName.matches( "main_#.*" ) ) { nameWithoutParameters = methodName; } else { nameWithoutParameters = pAbsoluteMethodName.substring( 0, pAbsoluteMethodName.lastIndexOf( "(" ) ); } int classIndex = nameWithoutParameters.lastIndexOf( "::" ); String absoluteClassName; // On rattache les fonctions � une classe fictive if ( classIndex == -1 ) { absoluteClassName = DEFAULT_CLASS_NAME; } else { absoluteClassName = nameWithoutParameters.substring( 0, nameWithoutParameters.lastIndexOf( "::" ) ); } ClassBO parent = getClass( absoluteClassName ); methodBO.setParent( parent ); methodBO.setLongFileName( pFileName ); } catch ( StringIndexOutOfBoundsException e ) { // cas d'un nom de m�thode mal form� - bug McCabe sur l'op�rateur C++ () methodBO = null; } return methodBO; } /** * {@inheritDoc} * * @see org.squale.squalix.util.parser.LanguageParser#getClass(java.lang.String) */ public ClassBO getClass( String pAbsoluteClassName ) { // On r�cup�re le nom de la classe StringBuffer context = new StringBuffer(); String className = geRelativeElementName( pAbsoluteClassName, context ); ClassBO classBO = new ClassBO( className ); AbstractComplexComponentBO current = classBO; // Le contexte est soit un nom de classe, soit un nom de package // On le d�compose enti�rement while ( context.length() > 0 ) { AbstractComplexComponentBO parent = null; String parentName = geRelativeElementName( context.toString(), context ); // On doit maintenant deviner si le parentName est une classe ou un namespace if ( isClassName( parentName ) ) { parent = new ClassBO( parentName ); } else { parent = new PackageBO( parentName ); } current.setParent( parent ); current = parent; } AbstractComplexComponentBO parent = null; // On r�cup�re le parent associ� if ( current instanceof ClassBO ) { parent = getPackage( DEFAULT_PACKAGE_NAME ); } else { parent = mProject; } current.setParent( parent ); return classBO; } /** * @param parentName nom de classe * @return true si le nom est consid�r� comme �tant une classe */ private boolean isClassName( String parentName ) { // Un nom est supps� �tre une classe s'il s'agit d'un template // ou s'il s'agit d'un nom de classe d�j� enregistr� return parentName.indexOf( '>' ) > 0 || mKnownClasses.contains( parentName ); } /** * Cr�e le package d�sign� par les param�tres. * * @param pPackageName le nom absolu du package * @return le PackageBO d�sign� par les param�tres. */ private PackageBO getPackage( String pPackageName ) { PackageBO packageBO = new PackageBO( getPackageName( pPackageName ) ); String parentName = getParentName( pPackageName ); if ( null == parentName ) { packageBO.setParent( mProject ); } else { PackageBO parent = getPackage( parentName ); packageBO.setParent( parent ); } return packageBO; } /** * Retourne la cha�ne pAbsoluteName avant le dernier "::" ou null si il n'y a pas de "::" * * @param pAbsoluteName le nom absolu du fils * @return le nom absolu du parent */ public String getParentName( String pAbsoluteName ) { String parent = null; int lastDot = pAbsoluteName.lastIndexOf( "::" ); if ( lastDot != -1 ) { parent = pAbsoluteName.substring( 0, lastDot ); } return parent; } /** * Retourne le nom du package le plus � droite * * @param pPackageName le nom absolu du package * @return le nom du dernier package */ private String getPackageName( String pPackageName ) { String[] splittingPackageName = pPackageName.split( "\\." ); return splittingPackageName[splittingPackageName.length - 1]; } /** * D�compose le nom enti�rement qualifi� d'une m�thode afin de r�cup�rer son nom relatif. * * @param pAbsoluteMethodName le nom enti�rement qualifi� d'une m�thode * @return le nom relatif de la m�thode */ protected String getMethodName( String pAbsoluteMethodName ) { String relativeMethodName = ""; if ( pAbsoluteMethodName.matches( STATIC_INITIALIZER_PATTERN ) ) { // S'il s'agit d'une zone d'initialisation statique, // on la consid�re comme une m�thode relativeMethodName = getStaticInitializerName( pAbsoluteMethodName ); } else if ( pAbsoluteMethodName.equals( "main" ) || pAbsoluteMethodName.matches( "main_#.*" ) ) { // cas des m�thodes 'main' C++ inscrite sans signature par McCabe relativeMethodName = pAbsoluteMethodName; } else { StringBuffer context = new StringBuffer(); relativeMethodName = geRelativeElementName( pAbsoluteMethodName.substring( 0, pAbsoluteMethodName.lastIndexOf( "(" ) ), context ); relativeMethodName += pAbsoluteMethodName.substring( pAbsoluteMethodName.lastIndexOf( "(" ) ); } return relativeMethodName; } /** * D�compose le nom enti�rement qualifi� d'une classe afin de r�cup�rer son nom relatif. * * @param pAbsoluteName le nom enti�rement qualifi� de la m�thode * @param pContext contexte de d�finition de la classe (soit une classe englobante soit un namespace) (ou le nom * enti�rement qualifi� de la m�thode sans les param�tres) * @return le nom relatif de la classe (ou de la m�thode sans ses param�tres) */ protected String geRelativeElementName( String pAbsoluteName, StringBuffer pContext ) { // normal pattern = .+ car il faut aussi prendre en compte les destructeurs // et la red�finition des op�rateurs String normalPattern = ".+$"; String specialPattern = ".*<.*>.*$"; String name = ""; pContext.setLength( 0 ); // On commence par tester sur le pattern des templates // car le pattern de class simple englobe tout // Cas d'une classe bas�e sur des templates if ( pAbsoluteName.matches( specialPattern ) ) { // Cas complexe utilisant des templates int dbpIndex = pAbsoluteName.lastIndexOf( "::" ); int gtIndex = pAbsoluteName.lastIndexOf( ">" ); if ( dbpIndex > gtIndex ) { // Si le dernier :: est apr�s le dernier > // On r�cup�re la cha�ne apr�s les :: name = pAbsoluteName.substring( dbpIndex + 2 ); pContext.append( pAbsoluteName.substring( 0, dbpIndex ) ); } else { // Sinon traitement sp�cial name = getSpecialCppName( pAbsoluteName, pContext ); } } else { // Cas simple d'une classe qui n'utilise pas de template if ( pAbsoluteName.matches( normalPattern ) ) { int index = pAbsoluteName.lastIndexOf( ":" ); name = pAbsoluteName.substring( index + 1 ); if ( index > 0 ) { pContext.append( pAbsoluteName.substring( 0, index - 1 ) ); } } else { // Ne devrait jamais arriver name = DEFAULT_CLASS_NAME; } } // Enfin, on nettoie le nom complet return clearName( name ); } /** * Traite les nom sp�ciaux, c'est-�-dire contenant des d�clarations ou utilisation de templates. * * @param pAbsoluteName le nom � traiter. * @param pContext contexte de d�finition de la classe (soit une classe englobante soit un namespace) * @return le nom simplement qualifi�. */ private String getSpecialCppName( String pAbsoluteName, StringBuffer pContext ) { int count = 0; String className = pAbsoluteName; for ( int i = 0; i < pAbsoluteName.length(); i++ ) { // On analyse chacun des caract�res if ( count == 0 && pAbsoluteName.charAt( i ) == ':' && pAbsoluteName.charAt( ++i ) == ':' ) { // Si on trouve :: en dehors d'un template, on r�cup�re ce qu'il y a apr�s les :: className = pAbsoluteName.substring( i + 1 ); pContext.append( pAbsoluteName.substring( 0, i - 1 ) ); } else if ( pAbsoluteName.charAt( i ) == '<' ) { // On entre dans un template, donc on attend la fin du template count++; } else if ( pAbsoluteName.charAt( i ) == '>' ) { count--; } } return className; } /** * R�cup�re le nom de la m�thode s'il s'agit d'une zone d'initialisation. * * @param pAbsoluteName le nom � traiter. * @return le nom simplement qualifi�. */ private String getStaticInitializerName( String pAbsoluteName ) { int index = pAbsoluteName.lastIndexOf( "." ); String result = pAbsoluteName.substring( ++index ); return result; } /** * Enl�ve les caract�res sp�ciaux � la fin d'un nom de classe, package, m�thode.<br> * Les caract�res sp�ciaux sont les s�parateurs Java et C++ : * <ul> * <li>.</li> * <li>:</li> * <li>$</li> * </ul> * Ainsi que les caract�res de chemins : * <ul> * <li>/</li> * <li>\</li> * </ul> * * @param pName le nom � nettoyer * @return le nom sans caract�re sp�cial en fin. */ public static String clearName( String pName ) { String newName = pName; String badEnd = ".*[/.:\\$#]$"; while ( newName.matches( badEnd ) ) { newName = newName.substring( 0, pName.length() - 1 ); } return newName; } }