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