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}