001package com.hfg.util; 002 003 004import java.io.DataInputStream; 005import java.io.IOException; 006import java.util.ArrayList; 007import java.util.Collections; 008import java.util.HashMap; 009import java.util.HashSet; 010import java.util.List; 011import java.util.Map; 012import java.util.Set; 013import javax.tools.JavaFileObject; 014import javax.tools.StandardJavaFileManager; 015import javax.tools.StandardLocation; 016import javax.tools.ToolProvider; 017 018//------------------------------------------------------------------------------ 019/** 020 General utility for exploring java classes. 021 <div> 022 @author J. Alex Taylor, hairyfatguy.com 023 </div> 024 */ 025//------------------------------------------------------------------------------ 026// com.hfg XML/HTML Coding Library 027// 028// This library is free software; you can redistribute it and/or 029// modify it under the terms of the GNU Lesser General Public 030// License as published by the Free Software Foundation; either 031// version 2.1 of the License, or (at your option) any later version. 032// 033// This library is distributed in the hope that it will be useful, 034// but WITHOUT ANY WARRANTY; without even the implied warranty of 035// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 036// Lesser General Public License for more details. 037// 038// You should have received a copy of the GNU Lesser General Public 039// License along with this library; if not, write to the Free Software 040// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 041// 042// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 043// jataylor@hairyfatguy.com 044//------------------------------------------------------------------------------ 045 046public class ClassExplorer 047{ 048 private Map<String, Set<String>> mIsAssignableFromMap; 049 050 //########################################################################### 051 // PUBLIC METHODS 052 //########################################################################### 053 054 //-------------------------------------------------------------------------- 055 public List<String> findExtendingClasses(Class inBaseClass) 056 throws IOException 057 { 058 List<String> extendingClassnames = new ArrayList<>(20); 059 recursiveFindExtendingClasses(inBaseClass.getName(), extendingClassnames); 060 061 return extendingClassnames; 062 } 063 064 //########################################################################### 065 // PRIVATE METHODS 066 //########################################################################### 067 068 //-------------------------------------------------------------------------- 069 private void recursiveFindExtendingClasses(String inBaseClass, List<String> inExtendingClasses) 070 throws IOException 071 { 072 Set<String> extendingClasses = getIsAssignableFromMap().get(inBaseClass); 073 if (extendingClasses != null) 074 { 075 inExtendingClasses.addAll(extendingClasses); 076 for (String classname : extendingClasses) 077 { 078 recursiveFindExtendingClasses(classname, inExtendingClasses); 079 } 080 } 081 } 082 083 //-------------------------------------------------------------------------- 084 private synchronized Map<String, Set<String>> getIsAssignableFromMap() 085 throws IOException 086 { 087 if (null == mIsAssignableFromMap) 088 { 089 mIsAssignableFromMap = buildCache(); 090 } 091 092 return mIsAssignableFromMap; 093 } 094 095 //-------------------------------------------------------------------------- 096 private Map<String, Set<String>> buildCache() 097 throws IOException 098 { 099 Map<String, Set<String>> assignableFromMap = new HashMap<>(); 100 101 final StandardJavaFileManager fileManager = ToolProvider.getSystemJavaCompiler().getStandardFileManager(null, null, null); 102 for (JavaFileObject javaFileObj : fileManager.list(StandardLocation.CLASS_PATH, "", Collections.singleton(JavaFileObject.Kind.CLASS), true)) 103 { 104 if (javaFileObj.getName().contains("ct.sym(")) 105 { 106 // Don't process the linking lib 107 continue; 108 } 109 110 DataInputStream stream = null; 111 try 112 { 113 stream = new DataInputStream(javaFileObj.openInputStream()); 114 BasicClassFileInfo classInfo = new BasicClassFileInfo(stream); 115 116 if (! classInfo.getSuperClassname().equals(Object.class.getName())) 117 { 118 Set<String> extendingOrImplementingClasses = assignableFromMap.get(classInfo.getSuperClassname()); 119 if (null == extendingOrImplementingClasses) 120 { 121 extendingOrImplementingClasses = new HashSet<>(10); 122 assignableFromMap.put(classInfo.getSuperClassname(), extendingOrImplementingClasses); 123 } 124 125 extendingOrImplementingClasses.add(classInfo.getClassname()); 126 } 127 128 String[] interfaces = classInfo.getInterfaceClassnames(); 129 if (interfaces != null) 130 { 131 for (String interfaceName : interfaces) 132 { 133 Set<String> extendingOrImplementingClasses = assignableFromMap.get(interfaceName); 134 if (null == extendingOrImplementingClasses) 135 { 136 extendingOrImplementingClasses = new HashSet<>(5); 137 assignableFromMap.put(interfaceName, extendingOrImplementingClasses); 138 } 139 140 extendingOrImplementingClasses.add(classInfo.getClassname()); 141 } 142 } 143 } 144 catch (Throwable e) 145 { 146// System.out.println(fullClassName); 147 } 148 finally 149 { 150 if (stream != null) 151 { 152 stream.close(); 153 } 154 } 155 } 156 157 return assignableFromMap; 158 } 159}