1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.kernel.util;
24  
25  import java.io.BufferedReader;
26  import java.io.File;
27  import java.io.FileReader;
28  import java.io.IOException;
29  import java.io.Reader;
30  import java.io.StreamTokenizer;
31  import java.io.StringReader;
32  
33  import java.util.ArrayList;
34  import java.util.HashSet;
35  import java.util.List;
36  import java.util.Set;
37  import java.util.regex.Matcher;
38  import java.util.regex.Pattern;
39  
40  /**
41   * <a href="ClassUtil.java.html"><b><i>View Source</i></b></a>
42   *
43   * @author Brian Wing Shun Chan
44   * @author Sandeep Soni
45   *
46   */
47  public class ClassUtil {
48  
49      public static Set<String> getClasses(File file) throws IOException {
50          String fileName = file.getName();
51  
52          if (fileName.endsWith(".java")) {
53              fileName = fileName.substring(0, fileName.length() - 5);
54          }
55  
56          return getClasses(new FileReader(file), fileName);
57      }
58  
59      public static Set<String> getClasses(Reader reader, String className)
60          throws IOException {
61  
62          Set<String> classes = new HashSet<String>();
63  
64          StreamTokenizer st = new StreamTokenizer(new BufferedReader(reader));
65  
66          _setupParseTableForAnnotationProcessing(st);
67  
68          while (st.nextToken() != StreamTokenizer.TT_EOF) {
69              if (st.ttype == StreamTokenizer.TT_WORD) {
70                  if (st.sval.equals("class") || st.sval.equals("enum") ||
71                      st.sval.equals("interface") ||
72                      st.sval.equals("@interface")) {
73  
74                      break;
75                  }
76                  else if (st.sval.startsWith("@")) {
77                      st.ordinaryChar(' ');
78                      st.wordChars('=', '=');
79  
80                      String[] las = _processAnnotation(st.sval, st);
81  
82                      for (int i = 0; i < las.length; i++) {
83                          classes.add(las[i]);
84                      }
85  
86                      _setupParseTableForAnnotationProcessing(st);
87                  }
88              }
89          }
90  
91          _setupParseTable(st);
92  
93          while (st.nextToken() != StreamTokenizer.TT_EOF) {
94              if (st.ttype == StreamTokenizer.TT_WORD) {
95                  if (st.sval.indexOf('.') >= 0) {
96                      classes.add(st.sval.substring(0, st.sval.indexOf('.')));
97                  }
98                  else {
99                      classes.add(st.sval);
100                 }
101             }
102             else if (st.ttype != StreamTokenizer.TT_NUMBER &&
103                      st.ttype != StreamTokenizer.TT_EOL) {
104 
105                 if (Character.isUpperCase((char)st.ttype)) {
106                     classes.add(String.valueOf((char)st.ttype));
107                 }
108             }
109         }
110 
111         classes.remove(className);
112 
113         return classes;
114     }
115 
116     public static boolean isSubclass(Class<?> a, Class<?> b) {
117         if (a == b) {
118             return true;
119         }
120 
121         if (a == null || b == null) {
122             return false;
123         }
124 
125         for (Class<?> x = a; x != null; x = x.getSuperclass()) {
126             if (x == b) {
127                 return true;
128             }
129 
130             if (b.isInterface()) {
131                 Class<?>[] interfaces = x.getInterfaces();
132 
133                 for (int i = 0; i < interfaces.length; i++) {
134                     if (isSubclass(interfaces[i], b)) {
135                         return true;
136                     }
137                 }
138             }
139         }
140 
141         return false;
142     }
143 
144     public static boolean isSubclass(Class<?> a, String s) {
145         if (a == null || s == null) {
146             return false;
147         }
148 
149         if (a.getName().equals(s)) {
150             return true;
151         }
152 
153         for (Class<?> x = a; x != null; x = x.getSuperclass()) {
154             if (x.getName().equals(s)) {
155                 return true;
156             }
157 
158             Class<?>[] interfaces = x.getInterfaces();
159 
160             for (int i = 0; i < interfaces.length; i++) {
161                 if (isSubclass(interfaces[i], s)) {
162                     return true;
163                 }
164             }
165         }
166 
167         return false;
168     }
169 
170     private static String[] _processAnnotation(String s, StreamTokenizer st)
171         throws IOException {
172 
173         s = s.trim();
174 
175         List<String> tokens = new ArrayList<String>();
176 
177         Matcher annotationNameMatcher = _ANNOTATION_NAME_REGEXP.matcher(s);
178         Matcher annotationParametersMatcher =
179             _ANNOTATION_PARAMETERS_REGEXP.matcher(s);
180 
181         if (annotationNameMatcher.matches()) {
182             String annotationName = annotationNameMatcher.group();
183 
184             tokens.add(annotationName.replace("@", ""));
185         }
186         else if (annotationParametersMatcher.matches()) {
187             if (!s.trim().endsWith(")")) {
188                 while (st.nextToken() != StreamTokenizer.TT_EOF) {
189                     if (st.ttype == StreamTokenizer.TT_WORD) {
190                         s += st.sval;
191                         if (s.trim().endsWith(")")) {
192                             break;
193                         }
194                     }
195                 }
196             }
197 
198             annotationParametersMatcher =
199                 _ANNOTATION_PARAMETERS_REGEXP.matcher(s);
200 
201             if (annotationParametersMatcher.matches()) {
202                 String annotationName =
203                     annotationParametersMatcher.group(1);
204                 String annotationParameters =
205                     annotationParametersMatcher.group(2);
206 
207                 tokens.add(annotationName.replace("@", ""));
208 
209                 tokens = _processAnnotationParameters(
210                     annotationParameters,tokens);
211             }
212         }
213 
214         return tokens.toArray(new String[tokens.size()]);
215     }
216 
217     private static List<String> _processAnnotationParameters(
218             String s, List<String> tokens)
219         throws IOException {
220 
221         StreamTokenizer st = new StreamTokenizer(new StringReader(s));
222 
223         _setupParseTable(st);
224 
225         while (st.nextToken() != StreamTokenizer.TT_EOF) {
226             if (st.ttype == StreamTokenizer.TT_WORD) {
227                 if (st.sval.indexOf('.') >= 0) {
228                     tokens.add(st.sval.substring(0, st.sval.indexOf('.')));
229                 }
230                 else {
231                     tokens.add(st.sval);
232                 }
233             }
234             else if ((st.ttype != StreamTokenizer.TT_NUMBER) &&
235                      (st.ttype != StreamTokenizer.TT_EOL)) {
236 
237                 if (Character.isUpperCase((char)st.ttype)) {
238                     tokens.add(String.valueOf((char)st.ttype));
239                 }
240             }
241         }
242 
243         return tokens;
244     }
245 
246     private static void _setupParseTable(StreamTokenizer st) {
247         st.resetSyntax();
248         st.slashSlashComments(true);
249         st.slashStarComments(true);
250         st.wordChars('a', 'z');
251         st.wordChars('A', 'Z');
252         st.wordChars('.', '.');
253         st.wordChars('0', '9');
254         st.wordChars('_', '_');
255         st.lowerCaseMode(false);
256         st.eolIsSignificant(false);
257         st.quoteChar('"');
258         st.quoteChar('\'');
259         st.parseNumbers();
260     }
261 
262     private static void _setupParseTableForAnnotationProcessing(
263         StreamTokenizer st) {
264 
265         _setupParseTable(st);
266 
267         st.wordChars('@', '@');
268         st.wordChars('(', '(');
269         st.wordChars(')', ')');
270         st.wordChars('{', '{');
271         st.wordChars('}', '}');
272         st.wordChars(',',',');
273     }
274 
275     private static final Pattern _ANNOTATION_NAME_REGEXP =
276         Pattern.compile("@(\\w+)$");
277 
278     private static final Pattern _ANNOTATION_PARAMETERS_REGEXP =
279         Pattern.compile("@(\\w+)\\({0,1}\\{{0,1}([^)}]+)\\}{0,1}\\){0,1}");
280 
281 }