package org.apache.maven.diagrams.connectors.classes.asm_parser;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.LinkedList;
import java.util.List;
import org.apache.maven.diagrams.connectors.classes.ClassDataSource;
import org.apache.maven.diagrams.connectors.classes.ClassDataSourceException;
import org.apache.maven.diagrams.connectors.classes.model.ClassModel;
import org.apache.maven.diagrams.connectors.classes.model.FieldModel;
import org.apache.maven.diagrams.connectors.classes.model.MethodModel;
import org.apache.maven.diagrams.connectors.classes.model.ModifierModel;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.MethodNode;
/**
* Implementation ClassDataSource that uses Asm library to parse the files and to find interclass dependencies
*
* @author <a href="mailto:ptab@newitech.com">Piotr Tabor</a>
* @version $Id$
*
*/
public class AsmClassDataSource implements ClassDataSource
{
@SuppressWarnings( "unchecked" )
public ClassModel translateToClassModel( Class c ) throws ClassDataSourceException
{
ClassReader classReader;
try
{
classReader =
new ClassReader( AsmClassDataSource.class.getResourceAsStream( "/" + c.getName().replace( ".", "/" )
+ ".class" ) );
}
catch ( IOException e )
{
throw new ClassDataSourceException( "Cannot instante class reader for:" + c.getName(), e );
}
ClassNode classNode = new ClassNode();
classReader.accept( classNode, 0 );
return translateClassNodeToClassModel( classNode );
}
public ClassModel translateToClassModel( InputStream is ) throws ClassDataSourceException
{
try
{
ClassReader classReader = new ClassReader( is );
ClassNode classNode = new ClassNode();
classReader.accept( classNode, 0 );
return translateClassNodeToClassModel( classNode );
}
catch ( IOException e )
{
throw new ClassDataSourceException( "Cannot instante class reader for given inputStream.", e );
}
}
public ClassModel translateToClassModel( String className ) throws ClassDataSourceException
{
try
{
ClassReader classReader =
new ClassReader( AsmClassDataSource.class.getResourceAsStream( "/" + className.replace( ".", "/" )
+ ".class" ) );
ClassNode classNode = new ClassNode();
classReader.accept( classNode, 0 );
return translateClassNodeToClassModel( classNode );
}
catch ( IOException e )
{
throw new ClassDataSourceException( "Cannot instante class reader for:" + className, e );
}
}
public ClassModel translateToClassModel( ClassLoader classLoader, String className )
throws ClassDataSourceException
{
try
{
String res = className.replace( ".", "/" ) + ".class";
InputStream is = classLoader.getResourceAsStream( res );
if ( is == null )
throw new ClassDataSourceException( "Cannot find resource :" + res );
ClassReader classReader = new ClassReader( is );
ClassNode classNode = new ClassNode();
classReader.accept( classNode, 0 );
return translateClassNodeToClassModel( classNode );
}
catch ( IOException e )
{
throw new ClassDataSourceException( "Cannot instante class reader for:" + className, e );
}
}
/* ==================================================================== */
/**
* ClassNode is internal (asm's) model. This function translates all available information form the classNode into
* classModel.
*/
private ClassModel translateClassNodeToClassModel( ClassNode classNode ) throws ClassDataSourceException
{
ClassModel classModel = new ClassModel();
classModel.setClassifiedName( classifiedNameToDotName( classNode.name ) );
if ( classNode.superName != null )
classModel.setSuperClassName( classifiedNameToDotName( classNode.superName ) );
else
classModel.setSuperClassName( null );
classModel.setFields( translateFields( classNode ) );
classModel.setMethods( translateMethods( classNode ) );
classModel.setInterfaces( translateInterfaces( classNode ) );
classModel.setInterface( ( classNode.access & Opcodes.ACC_INTERFACE ) > 0 );
return classModel;
}
/**
* The method translates informations about interfaces from classNode into list of qualified interface names (dots
* as separator)
*
*
* @param classNode -
* source asm's classNode
* @return list of qualified interface names.
*/
@SuppressWarnings( "unchecked" )
private List<String> translateInterfaces( ClassNode classNode )
{
List<String> result = new LinkedList<String>();
for ( String _interface : (List<String>) classNode.interfaces )
{
result.add( classifiedNameToDotName( _interface ) );
}
return result;
}
/**
* The method get's all informations about methods from classNode and builds list of MethodModel objects.
*
* @param classNode
* @return list of MethodModel objects (about all methods in classNode class)
*
* @throws ClassDataSourceException
*/
@SuppressWarnings( "unchecked" )
private List<MethodModel> translateMethods( ClassNode classNode ) throws ClassDataSourceException
{
List<MethodModel> result = new LinkedList<MethodModel>();
for ( MethodNode method : (List<MethodNode>) classNode.methods )
{
try
{
result.add( translateMethodNode( method, simpleClassName( classNode.name ) ) );
}
catch ( ParseException e )
{
throw new ClassDataSourceException( "Cannot translate MethodNode to MethodModel", e );
}
}
return result;
}
/**
* Translates methodNode (asm's) into MethodModel objects. Translates also <init> methods into constructors
* (using simpleClassName).
*
* @param simpleClassName -
* not qualified name of class that constains the methodNode method
*/
private MethodModel translateMethodNode( MethodNode method, String simpleClassName ) throws ParseException
{
MethodModel methodModel = new MethodModel();
methodModel.setName( method.name.equals( "<init>" ) ? simpleClassName : method.name );
methodModel.setModifiers( ModifierModel.accessContantsToModifiers( method.access ) );
DescriptionParser descriptionParser = new DescriptionParser( method.desc );
methodModel.setParams( descriptionParser.readParamsList() );
methodModel.setType( descriptionParser.readType() );
return methodModel;
}
/**
* The method get's all informations about fields from classNode and builds list of FieldModel objects.
*
* @param classNode
* @return list of FieldModel objects (about all fields in classNode class)
*
* @throws ClassDataSourceException
*/
@SuppressWarnings( "unchecked" )
private List<FieldModel> translateFields( ClassNode classNode ) throws ClassDataSourceException
{
List<FieldModel> result = new LinkedList<FieldModel>();
for ( FieldNode field : (List<FieldNode>) classNode.fields )
{
try
{
result.add( translateFieldNode( field ) );
}
catch ( ParseException e )
{
throw new ClassDataSourceException( "Cannot translate MethodNode to MethodModel", e );
}
}
return result;
}
/**
* Translates single FieldNode (asm's) into FieldModel
*
* @param field
* @return
* @throws ParseException
*/
private FieldModel translateFieldNode( FieldNode field ) throws ParseException
{
FieldModel fieldModel = new FieldModel();
fieldModel.setName( field.name );
fieldModel.setModifiers( ModifierModel.accessContantsToModifiers( field.access ) );
DescriptionParser descriptionParser = new DescriptionParser( field.desc );
fieldModel.setType( descriptionParser.readType() );
return fieldModel;
}
/**
* Translates the "/" notation of class name into "." notation. For example java/lang/String is transalted into
* java.lang.String
*
* @param nam
* to be translated
* @return
*/
protected String classifiedNameToDotName( String name )
{
return name.replace( '/', '.' );
}
/**
* Translates qualified (by slashes) class name into simpleClassName
*
* @param name
* @return
*/
protected String simpleClassName( String name )
{
int last = name.lastIndexOf( '/' ) + 1;
return name.substring( last );
}
}