001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.server;
016    
017    import com.liferay.portal.kernel.log.Log;
018    import com.liferay.portal.kernel.log.LogFactoryUtil;
019    import com.liferay.portal.kernel.util.IntegerWrapper;
020    import com.liferay.portal.kernel.util.ObjectValuePair;
021    import com.liferay.portal.kernel.util.StringBundler;
022    import com.liferay.portal.kernel.util.StringUtil;
023    
024    import java.lang.reflect.Field;
025    import java.lang.reflect.Modifier;
026    import java.lang.reflect.Proxy;
027    
028    import java.util.ArrayList;
029    import java.util.Collection;
030    import java.util.Collections;
031    import java.util.HashMap;
032    import java.util.HashSet;
033    import java.util.List;
034    import java.util.Map;
035    import java.util.Set;
036    
037    /**
038     * @author Igor Spasic
039     */
040    public class DeepNamedValueScanner {
041    
042            public DeepNamedValueScanner(String value) {
043                    _value = StringUtil.toLowerCase(value);
044            }
045    
046            public DeepNamedValueScanner(String value, boolean visit) {
047                    this(value);
048    
049                    _visitArrays = visit;
050                    _visitCollections = visit;
051                    _visitLists = visit;
052                    _visitSets = visit;
053                    _visitMaps = visit;
054                    _visitStaticFields = visit;
055            }
056    
057            public long getElapsedTime() {
058                    return _elapsedTime;
059            }
060    
061            public String[] getExcludedClassNames() {
062                    return _excludedClassNames;
063            }
064    
065            public String[] getExcludedNames() {
066                    return _excludedNames;
067            }
068    
069            public String[] getIncludedClassNames() {
070                    return _includedClassNames;
071            }
072    
073            public Object getMatchedValue() {
074                    return _matchedValue;
075            }
076    
077            public int getMatchingCount() {
078                    return _matchingCount;
079            }
080    
081            public int getSkipFirstCount() {
082                    return _skipFirstCount;
083            }
084    
085            public boolean isScanning() {
086                    return _scanning;
087            }
088    
089            public boolean isTrackUsageCount() {
090                    return _trackUsageCount;
091            }
092    
093            public boolean isVisitArrays() {
094                    return _visitArrays;
095            }
096    
097            public boolean isVisitCollectionss() {
098                    return _visitCollections;
099            }
100    
101            public boolean isVisitLists() {
102                    return _visitLists;
103            }
104    
105            public boolean isVisitMaps() {
106                    return _visitMaps;
107            }
108    
109            public boolean isVisitSets() {
110                    return _visitSets;
111            }
112    
113            public boolean isVisitStaticFields() {
114                    return _visitStaticFields;
115            }
116    
117            public void printStatistics(int topCount) {
118                    if (!_trackUsageCount) {
119                            return;
120                    }
121    
122                    System.out.println("-- names statistics --");
123    
124                    _printStatistics(_nameDatasets.values(), topCount);
125    
126                    System.out.println("-- types statistics --");
127    
128                    _printStatistics(_typeDatasets.values(), topCount);
129            }
130    
131            public boolean scan(Object target) throws Exception {
132                    _elapsedTime = System.currentTimeMillis();
133    
134                    _visitedIds = new HashSet<String>();
135    
136                    _scanning = true;
137    
138                    _scan(target);
139    
140                    _visitedIds = null;
141    
142                    _elapsedTime = System.currentTimeMillis() - _elapsedTime;
143    
144                    if (_log.isDebugEnabled()) {
145                            if (!_scanning) {
146                                    StringBundler sb = new StringBundler(5);
147    
148                                    sb.append("Deep named value scanner found ");
149                                    sb.append(_matchingCount);
150                                    sb.append(" matches in ");
151                                    sb.append(_elapsedTime);
152                                    sb.append(" ms");
153    
154                                    _log.debug(sb.toString());
155                            }
156                            else {
157                                    _log.debug("Deep named value scanner did not finish scanning");
158                            }
159                    }
160    
161                    return !_scanning;
162            }
163    
164            public void setExcludedClassNames(String... excludedClassNames) {
165                    _excludedClassNames = excludedClassNames;
166    
167                    StringUtil.lowerCase(excludedClassNames);
168            }
169    
170            public void setExcludedNames(String... excludedNames) {
171                    _excludedNames = excludedNames;
172    
173                    StringUtil.lowerCase(excludedNames);
174            }
175    
176            public void setIncludedClassNames(String... includedClassNames) {
177                    _includedClassNames = includedClassNames;
178    
179                    StringUtil.lowerCase(includedClassNames);
180            }
181    
182            public void setSkipFirstCount(int skipFirstCount) {
183                    _skipFirstCount = skipFirstCount;
184            }
185    
186            public void setTrackUsageCount(boolean trackUsageCount) {
187                    _trackUsageCount = trackUsageCount;
188    
189                    if (trackUsageCount) {
190                            _nameDatasets = new HashMap<String, Dataset>();
191                            _typeDatasets = new HashMap<String, Dataset>();
192                    }
193            }
194    
195            public void setVisitArrays(boolean visitArrays) {
196                    _visitArrays = visitArrays;
197            }
198    
199            public void setVisitCollections(boolean visitCollections) {
200                    _visitCollections = visitCollections;
201            }
202    
203            public void setVisitLists(boolean visitLists) {
204                    _visitLists = visitLists;
205            }
206    
207            public void setVisitMaps(boolean visitMaps) {
208                    _visitMaps = visitMaps;
209            }
210    
211            public void setVisitSets(boolean visitSets) {
212                    _visitSets = visitSets;
213            }
214    
215            public void setVisitStaticFields(boolean visitStaticFields) {
216                    _visitStaticFields = visitStaticFields;
217            }
218    
219            private void _incrementUsageCount(
220                    Map<String, Dataset> datasets, String name) {
221    
222                    Dataset dataset = datasets.get(name);
223    
224                    if (dataset == null) {
225                            dataset = new Dataset();
226    
227                            dataset.setKey(name);
228                            dataset.setValue(new IntegerWrapper());
229    
230                            datasets.put(name, dataset);
231                    }
232    
233                    IntegerWrapper integerWrapper = dataset.getValue();
234    
235                    integerWrapper.increment();
236            }
237    
238            private boolean _isAcceptClass(Class<?> targetClass) {
239                    String targetClassName = targetClass.getName();
240    
241                    targetClassName = StringUtil.toLowerCase(targetClassName);
242    
243                    if (targetClassName.startsWith("java.")) {
244                            return false;
245                    }
246    
247                    if (targetClassName.startsWith("org.eclipse.osgi.")) {
248                            return false;
249                    }
250    
251                    if (targetClassName.startsWith("sun.misc.")) {
252                            return false;
253                    }
254    
255                    if (targetClassName.contains("log")) {
256                            return false;
257                    }
258    
259                    if (_excludedClassNames != null) {
260                            for (String excludedClassName : _excludedClassNames) {
261                                    if (targetClassName.contains(excludedClassName)) {
262                                            return false;
263                                    }
264                            }
265                    }
266    
267                    if (_includedClassNames != null) {
268                            boolean accept = false;
269    
270                            for (String includedClassName : _includedClassNames) {
271                                    if (targetClassName.contains(includedClassName)) {
272                                            accept = true;
273    
274                                            break;
275                                    }
276                            }
277    
278                            if (!accept) {
279                                    return false;
280                            }
281                    }
282    
283                    if (_trackUsageCount) {
284                            _incrementUsageCount(_typeDatasets, targetClass.getName());
285                    }
286    
287                    return true;
288            }
289    
290            private boolean _isAcceptName(String name) {
291                    if (name == null) {
292                            return true;
293                    }
294    
295                    name = StringUtil.toLowerCase(name);
296    
297                    if (_excludedNames != null) {
298                            for (String excludedNames : _excludedNames) {
299                                    if (name.contains(excludedNames)) {
300                                            return false;
301                                    }
302                            }
303                    }
304    
305                    if (_trackUsageCount) {
306                            _incrementUsageCount(_nameDatasets, name);
307                    }
308    
309                    return true;
310            }
311    
312            private void _matchField(Object target, Field field, String name)
313                    throws IllegalAccessException {
314    
315                    if (name == null) {
316                            return;
317                    }
318    
319                    _matchingCount++;
320    
321                    name = StringUtil.toLowerCase(name);
322    
323                    if (name.contains(_value)) {
324                            if (_skipFirstCount > 0) {
325                                    _skipFirstCount--;
326    
327                                    return;
328                            }
329    
330                            field.setAccessible(true);
331    
332                            _matchedValue = field.get(target);
333    
334                            _scanning = false;
335                    }
336            }
337    
338            private void _matchName(Object value, String name) {
339                    if (name == null) {
340                            return;
341                    }
342    
343                    _matchingCount++;
344    
345                    name = StringUtil.toLowerCase(name);
346    
347                    if (name.contains(_value)) {
348                            if (_skipFirstCount > 0) {
349                                    _skipFirstCount--;
350    
351                                    return;
352                            }
353    
354                            _matchedValue = value;
355    
356                            _scanning = false;
357                    }
358            }
359    
360            private void _printStatistics(Collection<Dataset> datasets, int topCount) {
361                    List<Dataset> datasetsList = new ArrayList<Dataset>();
362    
363                    for (Dataset dataset : datasets) {
364                            datasetsList.add(dataset);
365                    }
366    
367                    Collections.sort(datasetsList);
368    
369                    for (Dataset dataset : datasetsList) {
370                            System.out.println(dataset.getValue() + " " + dataset.getKey());
371    
372                            topCount--;
373    
374                            if (topCount == 0) {
375                                    break;
376                            }
377                    }
378            }
379    
380            private Object _resolveJavaProxy(Object target)
381                    throws IllegalAccessException, NoSuchFieldException {
382    
383                    Class<?> targetClass = target.getClass();
384                    Class<?> targetSuperClass = targetClass.getSuperclass();
385    
386                    if ((targetSuperClass != null) &&
387                            targetSuperClass.equals(Proxy.class)) {
388    
389                            Field field = targetSuperClass.getDeclaredField("h");
390    
391                            field.setAccessible(true);
392    
393                            target = field.get(target);
394                    }
395    
396                    return target;
397            }
398    
399            private void _scan(Object target) throws Exception {
400                    if (target == null) {
401                            return;
402                    }
403    
404                    if (!_scanning) {
405                            return;
406                    }
407    
408                    target = _resolveJavaProxy(target);
409    
410                    String visitedId = null;
411    
412                    try {
413                            visitedId = String.valueOf(System.identityHashCode(target));
414    
415                            if (_visitedIds.contains(visitedId)) {
416                                    return;
417                            }
418                    }
419                    catch (Exception e) {
420                            return;
421                    }
422    
423                    _visitedIds.add(visitedId);
424    
425                    Class<?> targetClass = target.getClass();
426    
427                    if (targetClass.isArray()) {
428                            if (!_visitArrays) {
429                                    return;
430                            }
431    
432                            Class<?> componentTypeClass = targetClass.getComponentType();
433    
434                            if (componentTypeClass.isPrimitive() == false) {
435                                    Object[] array = (Object[])target;
436    
437                                    for (Object element : array) {
438                                            _scan(element);
439                                    }
440                            }
441                    }
442                    else if (_visitLists && (target instanceof List)) {
443                            _scanCollection((List<Object>)target);
444                    }
445                    else if (_visitMaps && (target instanceof Map)) {
446                            _scanMap((Map<Object, Object>)target);
447                    }
448                    else if (_visitSets && (target instanceof Set)) {
449                            _scanCollection((Set<Object>)target);
450                    }
451                    else if (_visitCollections && (target instanceof Collection)) {
452                            _scanCollection((Collection<Object>)target);
453                    }
454                    else {
455                            _scanObject(target);
456                    }
457            }
458    
459            private void _scanCollection(Collection<Object> collection)
460                    throws Exception {
461    
462                    for (Object element : collection) {
463                            if (!_scanning) {
464                                    break;
465                            }
466    
467                            _scan(element);
468                    }
469            }
470    
471            private void _scanMap(Map<Object, Object> map) throws Exception {
472                    Set<Map.Entry<Object, Object>> entrySet = map.entrySet();
473    
474                    for (Map.Entry<Object, Object> entry : entrySet) {
475                            if (!_scanning) {
476                                    break;
477                            }
478    
479                            Object key = entry.getKey();
480                            Object value = entry.getValue();
481    
482                            String name = null;
483    
484                            if (key != null) {
485                                    name = key.toString();
486                            }
487    
488                            if (_isAcceptName(name)) {
489                                    _matchName(value, name);
490                                    _scan(value);
491                            }
492                    }
493            }
494    
495            private void _scanObject(Object target) throws Exception {
496                    Class<?> targetClass = target.getClass();
497    
498                    if (!_isAcceptClass(targetClass)) {
499                            return;
500                    }
501    
502                    while (targetClass != null) {
503                            Field[] fields = targetClass.getDeclaredFields();
504    
505                            for (Field field : fields) {
506                                    if (!_scanning) {
507                                            break;
508                                    }
509    
510                                    if (!_visitStaticFields) {
511                                            if ((field.getModifiers() & Modifier.STATIC) != 0) {
512                                                    continue;
513                                            }
514                                    }
515    
516                                    String fieldName = field.getName();
517    
518                                    if (_isAcceptName(fieldName)) {
519                                            _matchField(target, field, fieldName);
520    
521                                            field.setAccessible(true);
522    
523                                            Object fieldValue = field.get(target);
524    
525                                            if (fieldValue != null) {
526                                                    _scan(fieldValue);
527                                            }
528                                    }
529                            }
530    
531                            targetClass = targetClass.getSuperclass();
532                    }
533            }
534    
535            private static Log _log = LogFactoryUtil.getLog(
536                    DeepNamedValueScanner.class);
537    
538            private long _elapsedTime;
539            private String[] _excludedClassNames;
540            private String[] _excludedNames;
541            private String[] _includedClassNames;
542            private Object _matchedValue;
543            private int _matchingCount;
544            private Map<String, Dataset> _nameDatasets;
545            private boolean _scanning;
546            private int _skipFirstCount;
547            private boolean _trackUsageCount;
548            private Map<String, Dataset> _typeDatasets;
549            private final String _value;
550            private boolean _visitArrays;
551            private boolean _visitCollections;
552            private Set<String> _visitedIds;
553            private boolean _visitLists;
554            private boolean _visitMaps;
555            private boolean _visitSets;
556            private boolean _visitStaticFields;
557    
558            private class Dataset
559                    extends ObjectValuePair<String, IntegerWrapper>
560                    implements Comparable<Dataset> {
561    
562                    @Override
563                    public int compareTo(Dataset dataset) {
564                            IntegerWrapper integerWrapper = dataset.getValue();
565    
566                            return integerWrapper.compareTo(getValue());
567                    }
568    
569            }
570    
571    }