| package org.apache.maven.shared.jar.classes; |
| |
| /* |
| * 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 org.apache.bcel.classfile.ConstantClass; |
| import org.apache.bcel.classfile.ConstantUtf8; |
| import org.apache.bcel.classfile.EmptyVisitor; |
| import org.apache.bcel.classfile.JavaClass; |
| import org.apache.commons.collections.list.SetUniqueList; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * Implementation of a BCEL class visitor that analyzes a class and collects imports. |
| */ |
| public class ImportVisitor |
| extends EmptyVisitor |
| { |
| /** |
| * The list of imports discovered. |
| */ |
| private List<String> imports; |
| |
| /** |
| * The Java class that is being analyzed. |
| */ |
| private JavaClass javaClass; |
| |
| /** |
| * Pattern to detect if the import is qualified and allows retrieval of the actual import name from the string via |
| * the group 1. |
| */ |
| private static final Pattern QUALIFIED_IMPORT_PATTERN = Pattern.compile( "L([a-zA-Z][a-zA-Z0-9\\.]+);" ); |
| |
| /** |
| * Pattern that checks whether a string is valid UTF-8. Imports that are not are ignored. |
| */ |
| private static final Pattern VALID_UTF8_PATTERN = Pattern.compile( "^[\\(\\)\\[A-Za-z0-9;/]+$" ); |
| |
| /** |
| * Create an Import visitor. |
| * |
| * @param javaClass the javaclass to work from |
| */ |
| public ImportVisitor( JavaClass javaClass ) |
| { |
| this.javaClass = javaClass; |
| |
| // Create a list that is guaranteed to be unique while retaining it's list qualities (LinkedHashSet does not |
| // expose the list interface even if natural ordering is retained) |
| this.imports = SetUniqueList.decorate( new ArrayList<String>() ); |
| } |
| |
| /** |
| * Get the list of discovered imports. |
| * |
| * @return Returns the imports. |
| */ |
| public List<String> getImports() |
| { |
| return imports; |
| } |
| |
| /** |
| * Find any formally declared import in the Constant Pool. |
| * |
| * @see org.apache.bcel.classfile.EmptyVisitor#visitConstantClass(org.apache.bcel.classfile.ConstantClass) |
| */ |
| public void visitConstantClass( ConstantClass constantClass ) |
| { |
| String name = constantClass.getBytes( javaClass.getConstantPool() ); |
| |
| // only strings with '/' character are to be considered. |
| if ( name.indexOf( '/' ) == -1 ) |
| { |
| return; |
| } |
| |
| name = name.replace( '/', '.' ); |
| |
| if ( name.endsWith( ".class" ) ) |
| { |
| name = name.substring( 0, name.length() - 6 ); |
| } |
| |
| Matcher mat = QUALIFIED_IMPORT_PATTERN.matcher( name ); |
| if ( mat.find() ) |
| { |
| this.imports.add( mat.group( 1 ) ); |
| } |
| else |
| { |
| this.imports.add( name ); |
| } |
| } |
| |
| /** |
| * Find any package class Strings in the UTF8 String Pool. |
| * |
| * @see org.apache.bcel.classfile.EmptyVisitor#visitConstantUtf8(org.apache.bcel.classfile.ConstantUtf8) |
| */ |
| public void visitConstantUtf8( ConstantUtf8 constantUtf8 ) |
| { |
| String ret = constantUtf8.getBytes().trim(); |
| |
| // empty strings are not class names. |
| if ( ret.length() <= 0 ) |
| { |
| return; |
| } |
| |
| // Only valid characters please. |
| if ( !VALID_UTF8_PATTERN.matcher( ret ).matches() ) |
| { |
| return; |
| } |
| |
| // only strings with '/' character are to be considered. |
| if ( ret.indexOf( '/' ) == -1 ) |
| { |
| return; |
| } |
| |
| // Strings that start with '/' are bad too |
| // Seen when Pool has regex patterns. |
| if ( ret.charAt( 0 ) == '/' ) |
| { |
| return; |
| } |
| |
| // Make string more class-like. |
| ret = ret.replace( '/', '.' ); |
| |
| // Double ".." indicates a bad class fail-fast. |
| // Seen when ConstantUTF8 Pool has regex patterns. |
| if ( ret.contains( ".." ) ) |
| { |
| return; |
| } |
| |
| Matcher mat = QUALIFIED_IMPORT_PATTERN.matcher( ret ); |
| char prefix = ret.charAt( 0 ); |
| |
| if ( prefix == '(' ) |
| { |
| // A Method Declaration. |
| |
| // Loop for each Qualified Class found. |
| while ( mat.find() ) |
| { |
| this.imports.add( mat.group( 1 ) ); |
| } |
| } |
| else |
| { |
| // A Variable Declaration. |
| if ( mat.find() ) |
| { |
| // Add a UTF8 Qualified Class reference. |
| this.imports.add( mat.group( 1 ) ); |
| } |
| else |
| { |
| // Add a simple Class reference. |
| this.imports.add( ret ); |
| } |
| } |
| } |
| } |