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 = StringPool.BLANK;
276
277 if (s.trim().endsWith(")")) {
278 annotationParameters = annotationParametersMatcher.group(3);
279 }
280 else {
281 int pos = s.indexOf('{');
282
283 if (pos != -1) {
284 annotationParameters += s.substring(pos + 1);
285 }
286
287 while (st.nextToken() != StreamTokenizer.TT_EOF) {
288 if (st.ttype != StreamTokenizer.TT_WORD) {
289 continue;
290 }
291
292 annotationParameters += st.sval;
293
294 String trimmedValue = StringUtil.trim(st.sval);
295
296 if (!trimmedValue.endsWith(")")) {
297 continue;
298 }
299
300 int closeParenthesesCount = StringUtil.count(
301 annotationParameters, ")");
302 int openParenthesesCount = StringUtil.count(
303 annotationParameters, "(");
304
305 if (closeParenthesesCount > openParenthesesCount) {
306 break;
307 }
308 }
309 }
310
311 tokens = _processAnnotationParameters(annotationParameters, tokens);
312 }
313
314 return tokens.toArray(new String[tokens.size()]);
315 }
316
317 private static List<String> _processAnnotationParameters(
318 String s, List<String> tokens)
319 throws IOException {
320
321 StreamTokenizer st = new StreamTokenizer(new UnsyncStringReader(s));
322
323 _setupParseTable(st);
324
325 while (st.nextToken() != StreamTokenizer.TT_EOF) {
326 if (st.ttype == StreamTokenizer.TT_WORD) {
327 if (st.sval.indexOf('.') >= 0) {
328 tokens.add(st.sval.substring(0, st.sval.indexOf('.')));
329 }
330 else {
331 tokens.add(st.sval);
332 }
333 }
334 else if ((st.ttype != StreamTokenizer.TT_NUMBER) &&
335 (st.ttype != StreamTokenizer.TT_EOL)) {
336
337 if (Character.isUpperCase((char)st.ttype)) {
338 tokens.add(String.valueOf((char)st.ttype));
339 }
340 }
341 }
342
343 return tokens;
344 }
345
346 private static void _setupParseTable(StreamTokenizer st) {
347 st.resetSyntax();
348 st.slashSlashComments(true);
349 st.slashStarComments(true);
350 st.wordChars('a', 'z');
351 st.wordChars('A', 'Z');
352 st.wordChars('.', '.');
353 st.wordChars('0', '9');
354 st.wordChars('_', '_');
355 st.lowerCaseMode(false);
356 st.eolIsSignificant(false);
357 st.quoteChar('"');
358 st.quoteChar('\'');
359 st.parseNumbers();
360 }
361
362 private static void _setupParseTableForAnnotationProcessing(
363 StreamTokenizer st) {
364
365 _setupParseTable(st);
366
367 st.wordChars('@', '@');
368 st.wordChars('(', '(');
369 st.wordChars(')', ')');
370 st.wordChars('{', '{');
371 st.wordChars('}', '}');
372 st.wordChars(',',',');
373 }
374
375 private static final Pattern _ANNOTATION_NAME_REGEXP = Pattern.compile(
376 "@(\\w+)\\.?(\\w*)$");
377
378 private static final Pattern _ANNOTATION_PARAMETERS_REGEXP =
379 Pattern.compile(
380 "@(\\w+)\\.?(\\w*)\\({0,1}\\{{0,1}([^)}]+)\\}{0,1}\\){0,1}");
381
382 private static final String _CLASS_EXTENSION = ".class";
383
384 private static final Log _log = LogFactoryUtil.getLog(ClassUtil.class);
385
386 }