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("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 boolean isSubclass(Class<?> a, Class<?> b) {
116         if (a == b) {
117             return true;
118         }
119 
120         if (a == null || b == null) {
121             return false;
122         }
123 
124         for (Class<?> x = a; x != null; x = x.getSuperclass()) {
125             if (x == b) {
126                 return true;
127             }
128 
129             if (b.isInterface()) {
130                 Class<?>[] interfaces = x.getInterfaces();
131 
132                 for (int i = 0; i < interfaces.length; i++) {
133                     if (isSubclass(interfaces[i], b)) {
134                         return true;
135                     }
136                 }
137             }
138         }
139 
140         return false;
141     }
142 
143     public static boolean isSubclass(Class<?> a, String s) {
144         if (a == null || s == null) {
145             return false;
146         }
147 
148         if (a.getName().equals(s)) {
149             return true;
150         }
151 
152         for (Class<?> x = a; x != null; x = x.getSuperclass()) {
153             if (x.getName().equals(s)) {
154                 return true;
155             }
156 
157             Class<?>[] interfaces = x.getInterfaces();
158 
159             for (int i = 0; i < interfaces.length; i++) {
160                 if (isSubclass(interfaces[i], s)) {
161                     return true;
162                 }
163             }
164         }
165 
166         return false;
167     }
168 
169     private static String[] _processAnnotation(String s, StreamTokenizer st)
170         throws IOException {
171 
172         s = s.trim();
173 
174         List<String> tokens = new ArrayList<String>();
175 
176         Matcher annotationNameMatcher = _ANNOTATION_NAME_REGEXP.matcher(s);
177         Matcher annotationParametersMatcher =
178             _ANNOTATION_PARAMETERS_REGEXP.matcher(s);
179 
180         if (annotationNameMatcher.matches()) {
181             String annotationName = annotationNameMatcher.group();
182 
183             tokens.add(annotationName.replace("@", ""));
184         }
185         else if (annotationParametersMatcher.matches()) {
186             if (!s.trim().endsWith(")")) {
187                 while (st.nextToken() != StreamTokenizer.TT_EOF) {
188                     if (st.ttype == StreamTokenizer.TT_WORD) {
189                         s += st.sval;
190                         if (s.trim().endsWith(")")) {
191                             break;
192                         }
193                     }
194                 }
195             }
196 
197             annotationParametersMatcher =
198                 _ANNOTATION_PARAMETERS_REGEXP.matcher(s);
199 
200             if (annotationParametersMatcher.matches()) {
201                 String annotationName =
202                     annotationParametersMatcher.group(1);
203                 String annotationParameters =
204                     annotationParametersMatcher.group(2);
205 
206                 tokens.add(annotationName.replace("@", ""));
207 
208                 tokens = _processAnnotationParameters(
209                     annotationParameters,tokens);
210             }
211         }
212 
213         return tokens.toArray(new String[tokens.size()]);
214     }
215 
216     private static List<String> _processAnnotationParameters(
217             String s, List<String> tokens)
218         throws IOException {
219 
220         StreamTokenizer st = new StreamTokenizer(new StringReader(s));
221 
222         _setupParseTable(st);
223 
224         while (st.nextToken() != StreamTokenizer.TT_EOF) {
225             if (st.ttype == StreamTokenizer.TT_WORD) {
226                 if (st.sval.indexOf('.') >= 0) {
227                     tokens.add(st.sval.substring(0, st.sval.indexOf('.')));
228                 }
229                 else {
230                     tokens.add(st.sval);
231                 }
232             }
233             else if ((st.ttype != StreamTokenizer.TT_NUMBER) &&
234                      (st.ttype != StreamTokenizer.TT_EOL)) {
235 
236                 if (Character.isUpperCase((char)st.ttype)) {
237                     tokens.add(String.valueOf((char)st.ttype));
238                 }
239             }
240         }
241 
242         return tokens;
243     }
244 
245     private static void _setupParseTable(StreamTokenizer st) {
246         st.resetSyntax();
247         st.slashSlashComments(true);
248         st.slashStarComments(true);
249         st.wordChars('a', 'z');
250         st.wordChars('A', 'Z');
251         st.wordChars('.', '.');
252         st.wordChars('0', '9');
253         st.wordChars('_', '_');
254         st.lowerCaseMode(false);
255         st.eolIsSignificant(false);
256         st.quoteChar('"');
257         st.quoteChar('\'');
258         st.parseNumbers();
259     }
260 
261     private static void _setupParseTableForAnnotationProcessing(
262         StreamTokenizer st) {
263 
264         _setupParseTable(st);
265 
266         st.wordChars('@', '@');
267         st.wordChars('(', '(');
268         st.wordChars(')', ')');
269         st.wordChars('{', '{');
270         st.wordChars('}', '}');
271         st.wordChars(',',',');
272     }
273 
274     private static final Pattern _ANNOTATION_NAME_REGEXP =
275         Pattern.compile("@(\\w+)$");
276 
277     private static final Pattern _ANNOTATION_PARAMETERS_REGEXP =
278         Pattern.compile("@(\\w+)\\({0,1}\\{{0,1}([^)}]+)\\}{0,1}\\){0,1}");
279 
280 }