/*
 * This file is part of FastClasspathScanner.
 * 
 * Author: Harith Elrufaie
 * 
 * Hosted at: https://github.com/lukehutch/fast-classpath-scanner
 * 
 * --
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Harith Elrufaie
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
 * documentation files (the "Software"), to deal in the Software without restriction, including without
 * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
 * conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial
 * portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
 * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
 * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
 * OR OTHER DEALINGS IN THE SOFTWARE.
 */
package io.github.lukehutch.fastclasspathscanner.classloaderhandler;

import java.io.File;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Map;
import java.util.Set;

import io.github.lukehutch.fastclasspathscanner.scanner.ClasspathFinder;
import io.github.lukehutch.fastclasspathscanner.utils.LogNode;
import io.github.lukehutch.fastclasspathscanner.utils.ReflectionUtils;

/**
 * Custom Class Loader Handler for OSGi Felix ClassLoader.
 *
 * The handler adds the bundle jar and all assocaited Bundle-Claspath jars into the {@link ClasspathFinder} scan
 * classpath.
 *
 * @author elrufaie
 */
public class FelixClassLoaderHandler implements ClassLoaderHandler {

    final Set<Object> bundles = new HashSet<>();

    @Override
    public boolean handle(final ClassLoader classLoader, final ClasspathFinder classpathFinder, final LogNode log)
            throws Exception {
        final List<ClassLoader> classLoaders = Arrays.asList(classLoader);
        for (Class<?> c = classLoader.getClass(); c != null; c = c.getSuperclass()) {
            if ("org.apache.felix.framework.BundleWiringImpl$BundleClassLoaderJava5".equals(c.getName()) ||
                "org.apache.felix.framework.BundleWiringImpl$BundleClassLoader".equals(c.getName())) {

                // Get the wiring for the ClassLoader's bundle
                final Object bundleWiring = ReflectionUtils.getFieldVal(classLoader, "m_wiring");
                addBundle(bundleWiring, classLoaders, classpathFinder, log);

                /*
                 * Finally, deal with any other bundles we might be wired to.
                 * Ideally we'd use the ScanSpec to narrow down the list of wires that we follow,
                 * but it doesn't seem to be available to us :-(
                 */
                
                final List requiredWires = (List)ReflectionUtils.invokeMethod(bundleWiring, "getRequiredWires", String.class, null);
                if (requiredWires != null) {
                    for (Object wire : requiredWires) {
                        final Object provider = ReflectionUtils.invokeMethod(wire,"getProviderWiring");
                        if (!bundles.contains(provider)) {
                            addBundle(provider, classLoaders, classpathFinder, log);
                        }
                    }
                }

                return true;
            }
        }
        return false;
    }

    private void addBundle(final Object bundleWiring, final List<ClassLoader> classLoaders, final ClasspathFinder classpathFinder, final LogNode log) throws Exception {
        // Track the bundles we've processed to prevent loops
        bundles.add(bundleWiring);

        // Get the revision for this wiring
        final Object revision = ReflectionUtils.invokeMethod(bundleWiring,"getRevision");
        // Get the contents
        final Object content = ReflectionUtils.invokeMethod(revision,"getContent");
        final String location = content != null ? getContentLocation(content) : null;
        if (location != null) {
            // Add the bundle object
            classpathFinder.addClasspathElement(location, classLoaders, log);

            // And any embedded content
            final List embeddedContent = (List)ReflectionUtils.invokeMethod(revision,"getContentPath");
            if (embeddedContent != null) {
                for (Object embedded : embeddedContent) {
                    if (embedded != content) {
                        final String embeddedLocation = embedded != null ? getContentLocation(embedded) : null;
                        if (embeddedLocation != null) {
                            classpathFinder.addClasspathElement(embeddedLocation, classLoaders, log);
                        }
                    }
                }
            }
        }
    }

    private String getContentLocation(final Object content) throws Exception {
        final File file = (File)ReflectionUtils.invokeMethod(content,"getFile");
        return file != null ? file.toURI().toString() : null;
    }
}
@@ -1458,89 +1458,12 @@
 til.
-Iterator;%0Aimport java.util.List;%0Aimport java.util.Map.Entry;%0Aimport java.util.Map
+List
 ;%0Aim
@@ -2460,11 +2460,8 @@
 e())
- %7C%7C
 %0A   
@@ -2472,16 +2472,23 @@
         
+     %7C%7C
  %22org.ap
@@ -3087,32 +3087,16 @@
      */%0A
-                
 %0A       
@@ -3114,16 +3114,19 @@
 nal List
+%3C?%3E
  require
@@ -3131,33 +3131,37 @@
 redWires = (List
-)
+%3C?%3E) 
 ReflectionUtils.
@@ -3186,16 +3186,40 @@
 eWiring,
+%0A                       
  %22getReq
@@ -3318,24 +3318,30 @@
        for (
+final 
 Object wire 
@@ -3441,16 +3441,17 @@
 od(wire,
+ 
 %22getProv
@@ -3848,16 +3848,28 @@
 Loaders,
+%0A           
  final C
@@ -4154,16 +4154,17 @@
 eWiring,
+ 
 %22getRevi
@@ -4260,32 +4260,33 @@
 Method(revision,
+ 
 %22getContent%22);%0A 
@@ -4580,16 +4580,19 @@
 nal List
+%3C?%3E
  embedde
@@ -4607,17 +4607,21 @@
  = (List
-)
+%3C?%3E) 
 Reflecti
@@ -4650,16 +4650,17 @@
 evision,
+ 
 %22getCont
@@ -4734,16 +4734,22 @@
    for (
+final 
 Object e
@@ -5302,16 +5302,17 @@
 = (File)
+ 
 Reflecti
@@ -5340,16 +5340,17 @@
 content,
+ 
 %22getFile

Clean up source