Merge topic 'checkstyle'
[controller.git] / opendaylight / adsal / northbound / bundlescanner / implementation / src / main / java / org / opendaylight / controller / northbound / bundlescanner / internal / BundleInfo.java
1 /**
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.controller.northbound.bundlescanner.internal;
10
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.Dictionary;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.regex.Pattern;
20
21 import org.osgi.framework.Bundle;
22 import org.osgi.framework.Constants;
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 /**
27  * BundleInfo holds information related to the bundle obtained during the
28  * bundle scan process.
29  */
30 /*package*/ class BundleInfo {
31     private static final Logger LOGGER = LoggerFactory.getLogger(BundleInfo.class);
32
33     private final Bundle bundle;
34     private final Map<String, Set<String>> annotatedClasses;
35     private final Set<String> exportPkgs;
36     private final Set<String> importPkgs;
37
38     public BundleInfo(Bundle bundle, Map<String, Set<String>> classes) {
39         this.bundle = bundle;
40         this.annotatedClasses = classes;
41         Dictionary<String, String> dict = bundle.getHeaders();
42         this.importPkgs = parsePackages(dict.get(Constants.IMPORT_PACKAGE));
43         this.exportPkgs = parsePackages(dict.get(Constants.EXPORT_PACKAGE));
44     }
45
46     @Override
47     public String toString() {
48         StringBuilder sb = new StringBuilder(super.toString());
49         sb.append("{name:").append(bundle.getSymbolicName())
50           .append(" id:").append(getId())
51           .append(" annotated-classes:").append(annotatedClasses)
52           .append(" imports:").append(importPkgs)
53           .append(" exports:").append(exportPkgs).append("}");
54         return sb.toString();
55     }
56
57     public Bundle getBundle() {
58         return bundle;
59     }
60
61     public long getId() {
62         return bundle.getBundleId();
63     }
64
65     public List<Class<?>> getAnnotatedClasses(Pattern pattern, Set<String> excludes) {
66         List<String> result = new ArrayList<String>();
67         for (Map.Entry<String, Set<String>> entry : annotatedClasses.entrySet()) {
68             if (matches(pattern, entry.getValue())) {
69                 result.add(entry.getKey());
70             }
71         }
72         return BundleScanner.loadClasses(result, bundle, excludes);
73     }
74
75     private boolean matches(Pattern pattern, Set<String> values) {
76         if (pattern == null) {
77             return true;
78         }
79         //LOGGER.debug("Matching: {} {}", pattern.toString(), values);
80         for (String s : values) {
81             if (pattern.matcher(s).find()) {
82                 return true;
83             }
84         }
85         return false;
86     }
87
88     /**
89      * Get classes with annotations matching a pattern
90      *
91      * @param allbundles - all bundles
92      * @param pattern - annotation pattern to match
93      * @param initBundle - the bundle which initiated this call
94      * @param excludes - set of class names to be excluded
95      *
96      * @return list of annotated classes matching the pattern
97      */
98     public List<Class<?>> getAnnotatedClasses(
99             Collection<BundleInfo> allbundles,
100             Pattern pattern, Bundle initBundle,
101             Set<String> excludes)
102     {
103         List<Class<?>> classes = getAnnotatedClasses(pattern, excludes);
104         processAnnotatedClassesInternal(this, allbundles, pattern,
105                 new HashSet<BundleInfo>(), classes, initBundle, excludes);
106         return classes;
107     }
108
109     private List<String> getExportedAnnotatedClasses(Pattern pattern) {
110         List<String> classes = new ArrayList<String>();
111         for (Map.Entry<String, Set<String>> entry : annotatedClasses.entrySet()) {
112             String cls = entry.getKey();
113             int idx = cls.lastIndexOf(".");
114             String pkg = (idx == -1 ? "" : cls.substring(0, idx));
115             // for a class to match, the package has to be exported and
116             // annotations should match the given pattern
117             if (exportPkgs.contains(pkg) && matches(pattern, entry.getValue())) {
118                 classes.add(cls);
119             }
120         }
121         if (LOGGER.isDebugEnabled()) {
122             LOGGER.debug("Found in bundle:{} exported classes:[{}]",
123                     getBundle().getSymbolicName(), classes);
124         }
125         return classes;
126     }
127
128     private static void processAnnotatedClassesInternal(
129             BundleInfo target,
130             Collection<BundleInfo> bundlesToScan,
131             Pattern pattern,
132             Collection<BundleInfo> visited,
133             List<Class<?>> classes,
134             Bundle initBundle, Set<String> excludes)
135     {
136         for (BundleInfo other : bundlesToScan) {
137             if (other.getId() == target.getId()) {
138                 continue;
139             }
140             if (target.isDependantOn(other)) {
141                 if (!visited.contains(other)) {
142                     classes.addAll(BundleScanner.loadClasses(
143                             other.getExportedAnnotatedClasses(pattern),
144                             initBundle, excludes));
145                     visited.add(other);
146                     processAnnotatedClassesInternal(other, bundlesToScan,
147                             pattern, visited, classes, initBundle, excludes);
148                 }
149             }
150         }
151     }
152
153     private boolean isDependantOn(BundleInfo other) {
154         for (String pkg : importPkgs) {
155             if (other.exportPkgs.contains(pkg)) {
156                 return true;
157             }
158         }
159         return false;
160     }
161
162     public List<BundleInfo> getDependencies(Collection<BundleInfo> bundles) {
163         List<BundleInfo> result = new ArrayList<BundleInfo>();
164         for(BundleInfo bundle : bundles) {
165             if (isDependantOn(bundle)) {
166                 result.add(bundle);
167             }
168         }
169         return result;
170     }
171
172
173     private static Set<String> parsePackages(String packageString) {
174         if (packageString == null) {
175             return Collections.emptySet();
176         }
177         String[] packages = packageString.split(",");
178         Set<String> result = new HashSet<String>();
179         for (int i=0; i<packages.length; i++) {
180             String[] nameAndAttrs = packages[i].split(";");
181             String packageName = nameAndAttrs[0].trim();
182             result.add(packageName);
183         }
184         return result;
185     }
186
187 }