/**
* 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.file;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.tools.ant.taskdefs.optional.jsp.JspNameMangler;
import org.squale.squalix.tools.compiling.jsp.configuration.JspCompilingConfiguration;
/**
* Utilitaire pour la manipulation des fichiers JSP
*/
public class JspFileUtility
{
/** Extension des pages JSP */
public static final String JSP_EXTENSION = ".jsp";
/**
* Java keywords
*/
private static final String JAVA_KEYWORDS[] =
{ "abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue",
"default", "do", "double", "else", "enum", "extends", "final", "finally", "float", "for", "goto", "if",
"implements", "import", "instanceof", "int", "interface", "long", "native", "new", "package", "private",
"protected", "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this",
"throws", "transient", "try", "void", "volatile", "while" };
/**
* Dans le cas des compilation des jsps, un nom de package ou classe interdit en java mais autoris� pour les JSPs
* est renomm�.<br/> Par exemple <b>package
* </p>
* sera renomm� en <b>package%</b> lors de la compilation des .jsp en .java.<br/> Ensuite les noms de contenant
* des caract�res sp�ciaux sont renomm�s.<br/> Par exemple notre <b>package%</b> sera renomm� en <b>package_0025</b>
*
* @param pJspPaths les r�pertoires contenant les jsps
* @param pFullName le nom absolue d'une classe jsp compil�e
* @throws IOException si erreur
* @return le nom absolu du fichier correpondant � la JSP
*/
public static String getAbsoluteJspFileName( List pJspPaths, String pFullName )
throws IOException
{
String fileName = null;
boolean search = true;
// On va examiner tout les noms de package ainsi que le nom
// de la classe
String[] decomposed = pFullName.split( "\\." );
String covertedDirName = "";
// Le premier package indique l'index du r�pertoire source dans la liste donn�e en param�tre
// ex : jsp -> index 0; jsp1 -> index1
for ( int i = 0; i < decomposed.length - 1; i++ )
{
covertedDirName += decomposed[i] + "/";
}
covertedDirName += decomposed[decomposed.length - 1] + JSP_EXTENSION;
String firstPackage = pFullName.substring( 0, pFullName.indexOf( "." ) );
File directory = new File( (String) pJspPaths.get( getJspDirectoryId( pJspPaths, decomposed[1] ) ) );
Set files = new HashSet();
files =
FileUtility.createRecursiveListOfFiles( directory, new ConvertFileNameFilter( directory, covertedDirName ),
files );
if ( files.size() > 0 )
{
fileName = (String) files.iterator().next();
}
return fileName;
}
/**
* @param pJspPaths la liste des noms des chemins vers les jsps
* @param pJspDir le nom du r�pertoire des jsps � trouver
* @return l'index du r�pertoire correspondant
*/
public static int getJspDirectoryId( List pJspPaths, String pJspDir )
{
// On r�cup�re l'index du r�pertoire pJspDir
int index = 0;
try
{
// On tente de r�cup�rer l'index du package
index = Integer.parseInt( pJspDir.replaceFirst( JspCompilingConfiguration.FIRST_PACKAGE, "" ) );
}
catch ( NumberFormatException nbfe )
{
// Si erreur c'est qu'il n'y a pas de chiffre � la fin du nom --> index 0
index = 0;
}
return index;
}
/**
* Converti un nom de classe ou package avec l'algorithme utilis� par JspC. Code copi� de JspNameMangler.java de ant
*
* @param pName le nom � convertir
* @return le nom converti
*/
public static String convertWithJspNameMangler( String pName )
{
// since we don't mangle extensions like the servlet does,
// we need to check for keywords as class names
String newName = pName;
for ( int i = 0; i < JspNameMangler.keywords.length && !newName.endsWith( "%" ); ++i )
{
if ( newName.equals( JspNameMangler.keywords[i] ) )
{
newName += "%";
}
}
// Fix for invalid characters. If you think of more add to the list.
StringBuffer modifiedClassName = new StringBuffer( newName.length() );
// first char is more restrictive than the rest
char firstChar = newName.charAt( 0 );
if ( Character.isJavaIdentifierStart( firstChar ) )
{
modifiedClassName.append( firstChar );
}
else
{
modifiedClassName.append( mangleChar( firstChar ) );
}
// this is the rest
for ( int i = 1; i < newName.length(); i++ )
{
char subChar = newName.charAt( i );
if ( Character.isJavaIdentifierPart( subChar ) )
{
modifiedClassName.append( subChar );
}
else
{
modifiedClassName.append( mangleChar( subChar ) );
}
}
return modifiedClassName.toString();
}
/**
* Algorithme de conversion de JspC. Code copi� de JspNameMangler.java de ant.
*
* @param ch char to mangle
* @return mangled string; 5 digit hex value
*/
private static final String mangleChar( char ch )
{
// La conversion du caract�re sp�cial sera de 6 caract�res
final int nbChar = 6;
char newChar = ch;
if ( newChar == File.separatorChar )
{
newChar = '/';
}
// Il converti en hexad�cimal pr�c�d� de "_"
// et d'autant de z�ro que n�cessaire pour compl�ter les 6
// caract�res
String s = Integer.toHexString( newChar );
int nzeros = ( nbChar - 1 ) - s.length();
char[] result = new char[nbChar];
result[0] = '_';
for ( int i = 1; i <= nzeros; ++i )
{
result[i] = '0';
}
int resultIndex = 0;
for ( int i = nzeros + 1; i < nbChar; ++i )
{
result[i] = s.charAt( resultIndex++ );
}
return new String( result );
}
/**
* Filtre sur les r�pertoires des JSPs pour ne trouver que le fichier correspondant au nom converti
*/
static class ConvertFileNameFilter
implements FileFilter
{
/** Chemin racine � ne pas convertir */
private File mRoot;
/** chemin de la jsp �ventuellement converti */
private String mPath;
/**
* Constructeur
*
* @param pRoot le chemin racine de la jsp
* @param pPath le nom du fichier �ventuellement converti par JspC
*/
public ConvertFileNameFilter( File pRoot, String pPath )
{
mRoot = pRoot;
mPath = pPath;
}
/**
* {@inheritDoc}
*
* @see java.io.FileFilter#accept(java.io.File)
*/
public boolean accept( File pathname )
{
boolean result = false;
if ( pathname.isDirectory() )
{
result = true;
}
else if ( pathname.isFile() && pathname.getName().endsWith( JSP_EXTENSION ) )
{
// si il s'agit d'une jsp, on v�rifie qu'elle ne correspond pas
// � la jsp convertie
String convertedPath = convertPath( pathname );
if ( convertedPath.endsWith( mPath ) )
{
result = true;
}
}
return result;
}
/**
* @param pPathname le nom du fichier � convertir
* @return le nom converti
*/
private String convertPath( File pPathname )
{
String result = "";
try
{
// On remplace les s�parateur de fichier pour avoir une coh�rence dans les comparaisons
String pathname = pPathname.getCanonicalPath().replaceAll( "\\\\", "/" );
String rootPathname = mRoot.getCanonicalPath().replaceAll( "\\\\", "/" );
// On r�cup�re le chemin relatif par rapport au r�pertoire racine de la jsp
String relativePath = pathname.replaceFirst( rootPathname, "" );
// On supprime le "/" au d�but et l'extension ".jsp" car il ne faut pas les convertir
relativePath = relativePath.substring( 1, relativePath.length() - JSP_EXTENSION.length() );
// On parcours l'ensemble du chemin pour convertir tous les r�pertoires
String[] paths = relativePath.split( "/" );
for ( int i = 0; i < paths.length - 1; i++ )
{
result += convertWithJspNameMangler( paths[i] ) + "/";
}
// On convertit le nom de la jsp et on lui rajoute son extension supprim�e plus haut
result += convertWithJspNameMangler( paths[paths.length - 1] ) + JSP_EXTENSION;
}
catch ( IOException e )
{
// Si une erreur survient, le fichier n'est pas correcte
result = "";
}
return result;
}
}
/**
* @param basePackageName root package for this jsp
* @param jspUri jsp relative path
* @return full classname for this jsp
* @see tomcat source code
*/
public static String getJspFullClassName( String basePackageName, String jspUri )
{
int iSep = jspUri.lastIndexOf( '/' ) + 1;
String className = makeJavaIdentifier( jspUri.substring( iSep ) );
String packageName = getServletPackageName( basePackageName, jspUri );
return packageName + "." + className;
}
/**
* Package name for the generated class is make up of the base package name, which is user settable, and the derived
* package name. The derived package name directly mirrors the file heirachy of the JSP page.
*
* @see tomcat source code
*/
public static String getServletPackageName( String basePackageName, String jspUri )
{
String result = basePackageName;
String dPackageName = getDerivedPackageName( jspUri );
if ( dPackageName.length() > 0 )
{
result += "." + dPackageName;
}
return result;
}
/**
* @param jspUri relative jsp path
* @return package's name converted by Tomcat
* @see Tomcat source code
*/
private static String getDerivedPackageName( String jspUri )
{
String derivedPackageName = null;
int iSep = jspUri.lastIndexOf( '/' );
derivedPackageName = ( iSep > 0 ) ? makeJavaPackage( jspUri.substring( 0, iSep ) ) : "";
return derivedPackageName;
}
/**
* Converts the given path to a Java package or fully-qualified class name
*
* @param path Path to convert
* @return Java package corresponding to the given path
*/
public static final String makeJavaPackage( String path )
{
String classNameComponents[] = path.split( "/" );
StringBuffer legalClassNames = new StringBuffer();
for ( int i = 0; i < classNameComponents.length; i++ )
{
legalClassNames.append( makeJavaIdentifier( classNameComponents[i] ) );
if ( i < classNameComponents.length - 1 )
{
legalClassNames.append( '.' );
}
}
return legalClassNames.toString();
}
/**
* Converts the given identifier to a legal Java identifier
*
* @param identifier Identifier to convert
* @return Legal Java identifier corresponding to the given identifier
*/
public static final String makeJavaIdentifier( String identifier )
{
StringBuffer modifiedIdentifier = new StringBuffer( identifier.length() );
if ( !Character.isJavaIdentifierStart( identifier.charAt( 0 ) ) )
{
modifiedIdentifier.append( '_' );
}
for ( int i = 0; i < identifier.length(); i++ )
{
char ch = identifier.charAt( i );
if ( Character.isJavaIdentifierPart( ch ) && ch != '_' )
{
modifiedIdentifier.append( ch );
}
else if ( ch == '.' )
{
modifiedIdentifier.append( '_' );
}
else
{
modifiedIdentifier.append( mangleChar( ch ) );
}
}
if ( isJavaKeyword( modifiedIdentifier.toString() ) )
{
modifiedIdentifier.append( '_' );
}
return modifiedIdentifier.toString();
}
/**
* Test whether the argument is a Java keyword
*
* @param key the key to test
* @return true if <code>key</code> is a java keyword
*/
public static boolean isJavaKeyword( String key )
{
int i = 0;
int j = JAVA_KEYWORDS.length;
while ( i < j )
{
int k = ( i + j ) / 2;
int result = JAVA_KEYWORDS[k].compareTo( key );
if ( result == 0 )
{
return true;
}
if ( result < 0 )
{
i = k + 1;
}
else
{
j = k;
}
}
return false;
}
}