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 int firstIndex = st.sval.indexOf('.');
095
096 if (firstIndex >= 0) {
097 classes.add(st.sval.substring(0, firstIndex));
098 }
099
100 int lastIndex = st.sval.lastIndexOf('.');
101
102 if (lastIndex >= 0) {
103 classes.add(st.sval.substring(lastIndex + 1));
104 }
105
106 if ((firstIndex < 0) && (lastIndex < 0)) {
107 classes.add(st.sval);
108 }
109 }
110 else if ((st.ttype != StreamTokenizer.TT_NUMBER) &&
111 (st.ttype != StreamTokenizer.TT_EOL)) {
112
113 if (Character.isUpperCase((char)st.ttype)) {
114 classes.add(String.valueOf((char)st.ttype));
115 }
116 }
117 }
118
119 classes.remove(className);
120
121 return classes;
122 }
123
124 public static String getClassName(Object object) {
125 if (object == null) {
126 return null;
127 }
128
129 Class<?> clazz = object.getClass();
130
131 return clazz.getName();
132 }
133
134 public static String getParentPath(
135 ClassLoader classLoader, String className) {
136
137 if (_log.isDebugEnabled()) {
138 _log.debug("Class name " + className);
139 }
140
141 if (!className.endsWith(_CLASS_EXTENSION)) {
142 className += _CLASS_EXTENSION;
143 }
144
145 className = StringUtil.replace(
146 className, CharPool.PERIOD, CharPool.SLASH);
147
148 className = StringUtil.replace(className, "/class", _CLASS_EXTENSION);
149
150 URL url = classLoader.getResource(className);
151
152 String path = null;
153
154 try {
155 path = url.getPath();
156
157 URI uri = new URI(path);
158
159 String scheme = uri.getScheme();
160
161 if (path.contains(StringPool.EXCLAMATION) &&
162 ((scheme == null) || (scheme.length() <= 1))) {
163
164 if (!path.startsWith(StringPool.SLASH)) {
165 path = StringPool.SLASH + path;
166 }
167 }
168 else {
169 path = uri.getPath();
170
171 if (path == null) {
172 path = url.getFile();
173 }
174 }
175 }
176 catch (URISyntaxException urise) {
177 path = url.getFile();
178 }
179
180 if (ServerDetector.isJBoss() || ServerDetector.isWildfly()) {
181 if (path.startsWith("file:") && !path.startsWith("file:/")) {
182 path = path.substring(5);
183
184 path = "file:/".concat(path);
185
186 path = StringUtil.replace(path, "%5C", StringPool.SLASH);
187 }
188 }
189
190 if (_log.isDebugEnabled()) {
191 _log.debug("Path " + path);
192 }
193
194 int pos = path.indexOf(className);
195
196 String parentPath = path.substring(0, pos);
197
198 if (parentPath.startsWith("jar:")) {
199 parentPath = parentPath.substring(4);
200 }
201
202 if (parentPath.startsWith("file:/")) {
203 parentPath = parentPath.substring(6);
204 }
205
206 if (_log.isDebugEnabled()) {
207 _log.debug("Parent path " + parentPath);
208 }
209
210 return parentPath;
211 }
212
213 public static boolean isSubclass(Class<?> a, Class<?> b) {
214 if (a == b) {
215 return true;
216 }
217
218 if ((a == null) || (b == null)) {
219 return false;
220 }
221
222 for (Class<?> x = a; x != null; x = x.getSuperclass()) {
223 if (x == b) {
224 return true;
225 }
226
227 if (b.isInterface()) {
228 Class<?>[] interfaceClasses = x.getInterfaces();
229
230 for (Class<?> interfaceClass : interfaceClasses) {
231 if (isSubclass(interfaceClass, b)) {
232 return true;
233 }
234 }
235 }
236 }
237
238 return false;
239 }
240
241 public static boolean isSubclass(Class<?> a, String s) {
242 if ((a == null) || (s == null)) {
243 return false;
244 }
245
246 if (a.getName().equals(s)) {
247 return true;
248 }
249
250 for (Class<?> x = a; x != null; x = x.getSuperclass()) {
251 if (x.getName().equals(s)) {
252 return true;
253 }
254
255 Class<?>[] interfaceClasses = x.getInterfaces();
256
257 for (Class<?> interfaceClass : interfaceClasses) {
258 if (isSubclass(interfaceClass, s)) {
259 return true;
260 }
261 }
262 }
263
264 return false;
265 }
266
267 private static String[] _processAnnotation(String s, StreamTokenizer st)
268 throws IOException {
269
270 s = s.trim();
271
272 List<String> tokens = new ArrayList<>();
273
274 Matcher annotationNameMatcher = _ANNOTATION_NAME_REGEXP.matcher(s);
275 Matcher annotationParametersMatcher =
276 _ANNOTATION_PARAMETERS_REGEXP.matcher(s);
277
278 if (annotationNameMatcher.matches()) {
279 tokens.add(annotationNameMatcher.group(1));
280 }
281 else if (annotationParametersMatcher.matches()) {
282 tokens.add(annotationParametersMatcher.group(1));
283
284 String annotationParameters = StringPool.BLANK;
285
286 if (s.trim().endsWith(")")) {
287 annotationParameters = annotationParametersMatcher.group(3);
288 }
289 else {
290 int pos = s.indexOf('{');
291
292 if (pos != -1) {
293 annotationParameters += s.substring(pos + 1);
294 }
295
296 while (st.nextToken() != StreamTokenizer.TT_EOF) {
297 if (st.ttype != StreamTokenizer.TT_WORD) {
298 continue;
299 }
300
301 annotationParameters += st.sval;
302
303 String trimmedValue = StringUtil.trim(st.sval);
304
305 if (!trimmedValue.endsWith(")")) {
306 continue;
307 }
308
309 int closeParenthesesCount = StringUtil.count(
310 annotationParameters, ")");
311 int openParenthesesCount = StringUtil.count(
312 annotationParameters, "(");
313
314 if (closeParenthesesCount > openParenthesesCount) {
315 break;
316 }
317 }
318 }
319
320 tokens = _processAnnotationParameters(annotationParameters, tokens);
321 }
322
323 return tokens.toArray(new String[tokens.size()]);
324 }
325
326 private static List<String> _processAnnotationParameters(
327 String s, List<String> tokens)
328 throws IOException {
329
330 StreamTokenizer st = new StreamTokenizer(new UnsyncStringReader(s));
331
332 _setupParseTable(st);
333
334 while (st.nextToken() != StreamTokenizer.TT_EOF) {
335 if (st.ttype == StreamTokenizer.TT_WORD) {
336 if (st.sval.indexOf('.') >= 0) {
337 tokens.add(st.sval.substring(0, st.sval.indexOf('.')));
338 }
339 else {
340 tokens.add(st.sval);
341 }
342 }
343 else if ((st.ttype != StreamTokenizer.TT_NUMBER) &&
344 (st.ttype != StreamTokenizer.TT_EOL)) {
345
346 if (Character.isUpperCase((char)st.ttype)) {
347 tokens.add(String.valueOf((char)st.ttype));
348 }
349 }
350 }
351
352 return tokens;
353 }
354
355 private static void _setupParseTable(StreamTokenizer st) {
356 st.resetSyntax();
357 st.slashSlashComments(true);
358 st.slashStarComments(true);
359 st.wordChars('a', 'z');
360 st.wordChars('A', 'Z');
361 st.wordChars('.', '.');
362 st.wordChars('0', '9');
363 st.wordChars('_', '_');
364 st.lowerCaseMode(false);
365 st.eolIsSignificant(false);
366 st.quoteChar('"');
367 st.quoteChar('\'');
368 st.parseNumbers();
369 }
370
371 private static void _setupParseTableForAnnotationProcessing(
372 StreamTokenizer st) {
373
374 _setupParseTable(st);
375
376 st.wordChars('@', '@');
377 st.wordChars('(', '(');
378 st.wordChars(')', ')');
379 st.wordChars('{', '{');
380 st.wordChars('}', '}');
381 st.wordChars(',',',');
382 }
383
384 private static final Pattern _ANNOTATION_NAME_REGEXP = Pattern.compile(
385 "@(\\w+)\\.?(\\w*)$");
386
387 private static final Pattern _ANNOTATION_PARAMETERS_REGEXP =
388 Pattern.compile(
389 "@(\\w+)\\.?(\\w*)\\({0,1}\\{{0,1}([^)}]+)\\}{0,1}\\){0,1}");
390
391 private static final String _CLASS_EXTENSION = ".class";
392
393 private static final Log _log = LogFactoryUtil.getLog(ClassUtil.class);
394
395 }