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