1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.kernel.util;
16  
17  import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
18  import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
19  import com.liferay.portal.kernel.log.Log;
20  import com.liferay.portal.kernel.log.LogFactoryUtil;
21  
22  import java.io.File;
23  import java.io.FileReader;
24  import java.io.IOException;
25  import java.io.Reader;
26  import java.io.StreamTokenizer;
27  
28  import java.net.URI;
29  import java.net.URISyntaxException;
30  import java.net.URL;
31  
32  import java.util.ArrayList;
33  import java.util.HashSet;
34  import java.util.List;
35  import java.util.Set;
36  import java.util.regex.Matcher;
37  import java.util.regex.Pattern;
38  
39  /**
40   * <a href="ClassUtil.java.html"><b><i>View Source</i></b></a>
41   *
42   * @author Brian Wing Shun Chan
43   * @author Sandeep Soni
44   */
45  public class ClassUtil {
46  
47      public static Set<String> getClasses(File file) throws IOException {
48          String fileName = file.getName();
49  
50          if (fileName.endsWith(".java")) {
51              fileName = fileName.substring(0, fileName.length() - 5);
52          }
53  
54          return getClasses(new FileReader(file), fileName);
55      }
56  
57      public static Set<String> getClasses(Reader reader, String className)
58          throws IOException {
59  
60          Set<String> classes = new HashSet<String>();
61  
62          StreamTokenizer st = new StreamTokenizer(
63              new UnsyncBufferedReader(reader));
64  
65          _setupParseTableForAnnotationProcessing(st);
66  
67          while (st.nextToken() != StreamTokenizer.TT_EOF) {
68              if (st.ttype == StreamTokenizer.TT_WORD) {
69                  if (st.sval.equals("class") || st.sval.equals("enum") ||
70                      st.sval.equals("interface") ||
71                      st.sval.equals("@interface")) {
72  
73                      break;
74                  }
75                  else if (st.sval.startsWith("@")) {
76                      st.ordinaryChar(' ');
77                      st.wordChars('=', '=');
78  
79                      String[] las = _processAnnotation(st.sval, st);
80  
81                      for (int i = 0; i < las.length; i++) {
82                          classes.add(las[i]);
83                      }
84  
85                      _setupParseTableForAnnotationProcessing(st);
86                  }
87              }
88          }
89  
90          _setupParseTable(st);
91  
92          while (st.nextToken() != StreamTokenizer.TT_EOF) {
93              if (st.ttype == StreamTokenizer.TT_WORD) {
94                  if (st.sval.indexOf('.') >= 0) {
95                      classes.add(st.sval.substring(0, st.sval.indexOf('.')));
96                  }
97                  else {
98                      classes.add(st.sval);
99                  }
100             }
101             else if (st.ttype != StreamTokenizer.TT_NUMBER &&
102                      st.ttype != StreamTokenizer.TT_EOL) {
103 
104                 if (Character.isUpperCase((char)st.ttype)) {
105                     classes.add(String.valueOf((char)st.ttype));
106                 }
107             }
108         }
109 
110         classes.remove(className);
111 
112         return classes;
113     }
114 
115     public static String getParentPath(
116         ClassLoader classLoader, String className) {
117 
118         if (_log.isDebugEnabled()) {
119             _log.debug("Class name " + className);
120         }
121 
122         if (!className.endsWith(_CLASS_EXTENSION)) {
123             className += _CLASS_EXTENSION;
124         }
125 
126         className = StringUtil.replace(
127             className, StringPool.PERIOD, StringPool.SLASH);
128 
129         className = StringUtil.replace(className, "/class", _CLASS_EXTENSION);
130 
131         URL url = classLoader.getResource(className);
132 
133         String path = null;
134 
135         try {
136             path = new URI(url.getPath()).getPath();
137         }
138         catch (URISyntaxException urise) {
139             path = url.getFile();
140         }
141 
142         if (_log.isDebugEnabled()) {
143             _log.debug("Path " + path);
144         }
145 
146         int pos = path.indexOf(className);
147 
148         String parentPath = path.substring(0, pos);
149 
150         if (parentPath.startsWith("jar:")) {
151             parentPath = parentPath.substring(4, parentPath.length());
152         }
153 
154         if (parentPath.startsWith("file:/")) {
155             parentPath = parentPath.substring(6, parentPath.length());
156         }
157 
158         if (_log.isDebugEnabled()) {
159             _log.debug("Parent path " + parentPath);
160         }
161 
162         return parentPath;
163     }
164 
165     public static boolean isSubclass(Class<?> a, Class<?> b) {
166         if (a == b) {
167             return true;
168         }
169 
170         if (a == null || b == null) {
171             return false;
172         }
173 
174         for (Class<?> x = a; x != null; x = x.getSuperclass()) {
175             if (x == b) {
176                 return true;
177             }
178 
179             if (b.isInterface()) {
180                 Class<?>[] interfaces = x.getInterfaces();
181 
182                 for (int i = 0; i < interfaces.length; i++) {
183                     if (isSubclass(interfaces[i], b)) {
184                         return true;
185                     }
186                 }
187             }
188         }
189 
190         return false;
191     }
192 
193     public static boolean isSubclass(Class<?> a, String s) {
194         if (a == null || s == null) {
195             return false;
196         }
197 
198         if (a.getName().equals(s)) {
199             return true;
200         }
201 
202         for (Class<?> x = a; x != null; x = x.getSuperclass()) {
203             if (x.getName().equals(s)) {
204                 return true;
205             }
206 
207             Class<?>[] interfaces = x.getInterfaces();
208 
209             for (int i = 0; i < interfaces.length; i++) {
210                 if (isSubclass(interfaces[i], s)) {
211                     return true;
212                 }
213             }
214         }
215 
216         return false;
217     }
218 
219     private static String[] _processAnnotation(String s, StreamTokenizer st)
220         throws IOException {
221 
222         s = s.trim();
223 
224         List<String> tokens = new ArrayList<String>();
225 
226         Matcher annotationNameMatcher = _ANNOTATION_NAME_REGEXP.matcher(s);
227         Matcher annotationParametersMatcher =
228             _ANNOTATION_PARAMETERS_REGEXP.matcher(s);
229 
230         if (annotationNameMatcher.matches()) {
231             String annotationName = annotationNameMatcher.group();
232 
233             tokens.add(annotationName.replace("@", ""));
234         }
235         else if (annotationParametersMatcher.matches()) {
236             if (!s.trim().endsWith(")")) {
237                 while (st.nextToken() != StreamTokenizer.TT_EOF) {
238                     if (st.ttype == StreamTokenizer.TT_WORD) {
239                         s += st.sval;
240                         if (s.trim().endsWith(")")) {
241                             break;
242                         }
243                     }
244                 }
245             }
246 
247             annotationParametersMatcher =
248                 _ANNOTATION_PARAMETERS_REGEXP.matcher(s);
249 
250             if (annotationParametersMatcher.matches()) {
251                 String annotationName =
252                     annotationParametersMatcher.group(1);
253                 String annotationParameters =
254                     annotationParametersMatcher.group(2);
255 
256                 tokens.add(annotationName.replace("@", ""));
257 
258                 tokens = _processAnnotationParameters(
259                     annotationParameters,tokens);
260             }
261         }
262 
263         return tokens.toArray(new String[tokens.size()]);
264     }
265 
266     private static List<String> _processAnnotationParameters(
267             String s, List<String> tokens)
268         throws IOException {
269 
270         StreamTokenizer st = new StreamTokenizer(new UnsyncStringReader(s));
271 
272         _setupParseTable(st);
273 
274         while (st.nextToken() != StreamTokenizer.TT_EOF) {
275             if (st.ttype == StreamTokenizer.TT_WORD) {
276                 if (st.sval.indexOf('.') >= 0) {
277                     tokens.add(st.sval.substring(0, st.sval.indexOf('.')));
278                 }
279                 else {
280                     tokens.add(st.sval);
281                 }
282             }
283             else if ((st.ttype != StreamTokenizer.TT_NUMBER) &&
284                      (st.ttype != StreamTokenizer.TT_EOL)) {
285 
286                 if (Character.isUpperCase((char)st.ttype)) {
287                     tokens.add(String.valueOf((char)st.ttype));
288                 }
289             }
290         }
291 
292         return tokens;
293     }
294 
295     private static void _setupParseTable(StreamTokenizer st) {
296         st.resetSyntax();
297         st.slashSlashComments(true);
298         st.slashStarComments(true);
299         st.wordChars('a', 'z');
300         st.wordChars('A', 'Z');
301         st.wordChars('.', '.');
302         st.wordChars('0', '9');
303         st.wordChars('_', '_');
304         st.lowerCaseMode(false);
305         st.eolIsSignificant(false);
306         st.quoteChar('"');
307         st.quoteChar('\'');
308         st.parseNumbers();
309     }
310 
311     private static void _setupParseTableForAnnotationProcessing(
312         StreamTokenizer st) {
313 
314         _setupParseTable(st);
315 
316         st.wordChars('@', '@');
317         st.wordChars('(', '(');
318         st.wordChars(')', ')');
319         st.wordChars('{', '{');
320         st.wordChars('}', '}');
321         st.wordChars(',',',');
322     }
323 
324     private static final Pattern _ANNOTATION_NAME_REGEXP =
325         Pattern.compile("@(\\w+)$");
326 
327     private static final Pattern _ANNOTATION_PARAMETERS_REGEXP =
328         Pattern.compile("@(\\w+)\\({0,1}\\{{0,1}([^)}]+)\\}{0,1}\\){0,1}");
329 
330     private static final String _CLASS_EXTENSION = ".class";
331 
332     private static Log _log = LogFactoryUtil.getLog(ClassUtil.class);
333 
334 }