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