001
014
015 package com.liferay.portal.kernel.util;
016
017 import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
018 import com.liferay.portal.kernel.io.unsync.UnsyncStringReader;
019 import com.liferay.portal.kernel.log.Log;
020 import com.liferay.portal.kernel.log.LogFactoryUtil;
021
022 import java.io.File;
023 import java.io.FileReader;
024 import java.io.IOException;
025 import java.io.Reader;
026 import java.io.StreamTokenizer;
027
028 import java.net.URI;
029 import java.net.URISyntaxException;
030 import java.net.URL;
031
032 import java.util.ArrayList;
033 import java.util.HashSet;
034 import java.util.List;
035 import java.util.Set;
036 import java.util.regex.Matcher;
037 import java.util.regex.Pattern;
038
039
043 public class ClassUtil {
044
045 public static Set<String> getClasses(File file) throws IOException {
046 String fileName = file.getName();
047
048 if (fileName.endsWith(".java")) {
049 fileName = fileName.substring(0, fileName.length() - 5);
050 }
051
052 return getClasses(
053 new UnsyncBufferedReader(new FileReader(file)), fileName);
054 }
055
056 public static Set<String> getClasses(Reader reader, String className)
057 throws IOException {
058
059 Set<String> classes = new HashSet<>();
060
061 StreamTokenizer st = new StreamTokenizer(reader);
062
063 _setupParseTableForAnnotationProcessing(st);
064
065 while (st.nextToken() != StreamTokenizer.TT_EOF) {
066 if (st.ttype == StreamTokenizer.TT_WORD) {
067 if (st.sval.equals("class") || st.sval.equals("enum") ||
068 st.sval.equals("interface") ||
069 st.sval.equals("@interface")) {
070
071 break;
072 }
073 else if (st.sval.startsWith("@")) {
074 st.ordinaryChar(' ');
075 st.wordChars('=', '=');
076 st.wordChars('+', '+');
077
078 String[] annotationClasses = _processAnnotation(
079 st.sval, st);
080
081 for (String annotationClass : annotationClasses) {
082 classes.add(annotationClass);
083 }
084
085 _setupParseTableForAnnotationProcessing(st);
086 }
087 }
088 }
089
090 _setupParseTable(st);
091
092 while (st.nextToken() != StreamTokenizer.TT_EOF) {
093 if (st.ttype == StreamTokenizer.TT_WORD) {
094 if (st.sval.indexOf('.') >= 0) {
095 classes.add(st.sval.substring(0, st.sval.indexOf('.')));
096 }
097 else {
098 classes.add(st.sval);
099 }
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 String getClassName(Object object) {
116 if (object == null) {
117 return null;
118 }
119
120 Class<?> clazz = object.getClass();
121
122 return clazz.getName();
123 }
124
125 public static String getParentPath(
126 ClassLoader classLoader, String className) {
127
128 if (_log.isDebugEnabled()) {
129 _log.debug("Class name " + className);
130 }
131
132 if (!className.endsWith(_CLASS_EXTENSION)) {
133 className += _CLASS_EXTENSION;
134 }
135
136 className = StringUtil.replace(
137 className, CharPool.PERIOD, CharPool.SLASH);
138
139 className = StringUtil.replace(className, "/class", _CLASS_EXTENSION);
140
141 URL url = classLoader.getResource(className);
142
143 String path = null;
144
145 try {
146 path = url.getPath();
147
148 URI uri = new URI(path);
149
150 String scheme = uri.getScheme();
151
152 if (path.contains(StringPool.EXCLAMATION) &&
153 ((scheme == null) || (scheme.length() <= 1))) {
154
155 if (!path.startsWith(StringPool.SLASH)) {
156 path = StringPool.SLASH + path;
157 }
158 }
159 else {
160 path = uri.getPath();
161
162 if (path == null) {
163 path = url.getFile();
164 }
165 }
166 }
167 catch (URISyntaxException urise) {
168 path = url.getFile();
169 }
170
171 if (ServerDetector.isJBoss() || ServerDetector.isWildfly()) {
172 if (path.startsWith("file:") && !path.startsWith("file:/")) {
173 path = path.substring(5);
174
175 path = "file:/".concat(path);
176
177 path = StringUtil.replace(path, "%5C", StringPool.SLASH);
178 }
179 }
180
181 if (_log.isDebugEnabled()) {
182 _log.debug("Path " + path);
183 }
184
185 int pos = path.indexOf(className);
186
187 String parentPath = path.substring(0, pos);
188
189 if (parentPath.startsWith("jar:")) {
190 parentPath = parentPath.substring(4);
191 }
192
193 if (parentPath.startsWith("file:/")) {
194 parentPath = parentPath.substring(6);
195 }
196
197 if (_log.isDebugEnabled()) {
198 _log.debug("Parent path " + parentPath);
199 }
200
201 return parentPath;
202 }
203
204 public static boolean isSubclass(Class<?> a, Class<?> b) {
205 if (a == b) {
206 return true;
207 }
208
209 if ((a == null) || (b == null)) {
210 return false;
211 }
212
213 for (Class<?> x = a; x != null; x = x.getSuperclass()) {
214 if (x == b) {
215 return true;
216 }
217
218 if (b.isInterface()) {
219 Class<?>[] interfaceClasses = x.getInterfaces();
220
221 for (Class<?> interfaceClass : interfaceClasses) {
222 if (isSubclass(interfaceClass, b)) {
223 return true;
224 }
225 }
226 }
227 }
228
229 return false;
230 }
231
232 public static boolean isSubclass(Class<?> a, String s) {
233 if ((a == null) || (s == null)) {
234 return false;
235 }
236
237 if (a.getName().equals(s)) {
238 return true;
239 }
240
241 for (Class<?> x = a; x != null; x = x.getSuperclass()) {
242 if (x.getName().equals(s)) {
243 return true;
244 }
245
246 Class<?>[] interfaceClasses = x.getInterfaces();
247
248 for (Class<?> interfaceClass : interfaceClasses) {
249 if (isSubclass(interfaceClass, s)) {
250 return true;
251 }
252 }
253 }
254
255 return false;
256 }
257
258 private static String[] _processAnnotation(String s, StreamTokenizer st)
259 throws IOException {
260
261 s = s.trim();
262
263 List<String> tokens = new ArrayList<>();
264
265 Matcher annotationNameMatcher = _ANNOTATION_NAME_REGEXP.matcher(s);
266 Matcher annotationParametersMatcher =
267 _ANNOTATION_PARAMETERS_REGEXP.matcher(s);
268
269 if (annotationNameMatcher.matches()) {
270 tokens.add(annotationNameMatcher.group(1));
271 }
272 else if (annotationParametersMatcher.matches()) {
273 tokens.add(annotationParametersMatcher.group(1));
274
275 String annotationParameters = null;
276
277 if (s.trim().endsWith(")")) {
278 annotationParameters = annotationParametersMatcher.group(3);
279 }
280 else {
281 StringBundler sb = new StringBundler();
282
283 int pos = s.indexOf('{');
284
285 if (pos != -1) {
286 sb.append(s.substring(pos + 1));
287 }
288
289 while (st.nextToken() != StreamTokenizer.TT_EOF) {
290 if (st.ttype == StreamTokenizer.TT_WORD) {
291 sb.append(st.sval);
292
293 if (st.sval.trim().endsWith(")")) {
294 break;
295 }
296 }
297 }
298
299 annotationParameters = sb.toString();
300 }
301
302 tokens = _processAnnotationParameters(annotationParameters, tokens);
303 }
304
305 return tokens.toArray(new String[tokens.size()]);
306 }
307
308 private static List<String> _processAnnotationParameters(
309 String s, List<String> tokens)
310 throws IOException {
311
312 StreamTokenizer st = new StreamTokenizer(new UnsyncStringReader(s));
313
314 _setupParseTable(st);
315
316 while (st.nextToken() != StreamTokenizer.TT_EOF) {
317 if (st.ttype == StreamTokenizer.TT_WORD) {
318 if (st.sval.indexOf('.') >= 0) {
319 tokens.add(st.sval.substring(0, st.sval.indexOf('.')));
320 }
321 else {
322 tokens.add(st.sval);
323 }
324 }
325 else if ((st.ttype != StreamTokenizer.TT_NUMBER) &&
326 (st.ttype != StreamTokenizer.TT_EOL)) {
327
328 if (Character.isUpperCase((char)st.ttype)) {
329 tokens.add(String.valueOf((char)st.ttype));
330 }
331 }
332 }
333
334 return tokens;
335 }
336
337 private static void _setupParseTable(StreamTokenizer st) {
338 st.resetSyntax();
339 st.slashSlashComments(true);
340 st.slashStarComments(true);
341 st.wordChars('a', 'z');
342 st.wordChars('A', 'Z');
343 st.wordChars('.', '.');
344 st.wordChars('0', '9');
345 st.wordChars('_', '_');
346 st.lowerCaseMode(false);
347 st.eolIsSignificant(false);
348 st.quoteChar('"');
349 st.quoteChar('\'');
350 st.parseNumbers();
351 }
352
353 private static void _setupParseTableForAnnotationProcessing(
354 StreamTokenizer st) {
355
356 _setupParseTable(st);
357
358 st.wordChars('@', '@');
359 st.wordChars('(', '(');
360 st.wordChars(')', ')');
361 st.wordChars('{', '{');
362 st.wordChars('}', '}');
363 st.wordChars(',',',');
364 }
365
366 private static final Pattern _ANNOTATION_NAME_REGEXP = Pattern.compile(
367 "@(\\w+)\\.?(\\w*)$");
368
369 private static final Pattern _ANNOTATION_PARAMETERS_REGEXP =
370 Pattern.compile(
371 "@(\\w+)\\.?(\\w*)\\({0,1}\\{{0,1}([^)}]+)\\}{0,1}\\){0,1}");
372
373 private static final String _CLASS_EXTENSION = ".class";
374
375 private static final Log _log = LogFactoryUtil.getLog(ClassUtil.class);
376
377 }