/*
Impl Notes:
We add the bytecode source and the "reload" the class, which causes the
BshClassLoader to be initialized and create a DiscreteFilesClassLoader
for the bytecode.

@exception ClassPathException can be thrown by reloadClasses
16813
@Override
    public Class defineClass( String name, byte [] code )
    {
        baseClassPath.setClassSource( name, new GeneratedClassSource( code ) );
        try {
            reloadClasses( new String [] { name } );
        } catch ( ClassPathException e ) {
            throw new bsh.InterpreterError("defineClass: "+e, e);
        }
        return classForName( name );
    }
996987
private static Class<?> loadColtClass(String className) 
            throws ClassNotFoundException { 
        try {
            Class<?> clazz = 
                Class.forName(className);
            return clazz;
            
        } catch (ClassNotFoundException cnfe) {

            // If we see a CNFE, don't immediately give up.  It's most likely
            // that this class is being invoked from inside a .jar, so see if
            // the user specified where COLT is manually.
            String coltProp = System.getProperty("colt.path");
            
            // if not, rethrow since the class does not exist
            if (coltProp == null)
                throw cnfe;

            File coltJarFile = new File(coltProp);
            try {
                // Otherwise, try to load the class from the specified .jar
                // file.  If the ClassLoader for loading all COLT classes has
                // not yet been initialized, do so now.  We need to maintain
                // only one class loader for all COLT classes, otherwise the
                // reflective invocation will fail; the constructor type system
                // does not work correctly if the parameter objects have been
                // loaded from a different class loader. --jurgens
                if (coltClassLoader == null) {
                    coltClassLoader = 
                        new java.net.URLClassLoader(
                        new java.net.URL[] { coltJarFile.toURI().toURL() });
                }

                Class<?> clazz = Class.forName(className, true,
                                               coltClassLoader);
                return clazz;
            } catch (Exception e) {
                // fall through and rethrow original
            }
            
            throw cnfe;
        }
    }
556120
public boolean reloadIsModified()
  {
    if (_classIsModified) {
      return true;
    }

    if (! _hasJNIReload || ! _classPath.canRead()) {
      return true;
    }
    
    try {
      long length = _classPath.length();

      Class<?> cl = _clRef != null ? _clRef.get() : null;

      if (cl == null) {
        return false;
      }

      /*
      if (cl.isAnnotationPresent(RequireReload.class))
        return true;
        */

      byte []bytecode = new byte[(int) length];
      try (InputStream is = _classPath.inputStream()) {
        IoUtil.readAll(is, bytecode, 0, bytecode.length);
      }

      int result = reloadNative(cl, bytecode, 0, bytecode.length);

      if (result != 0) {
        _classIsModified = true;
        return true;
      }

      // XXX: setDependPath(_classPath);

      if (_sourcePath != null) {
        _sourceLastModified = _sourcePath.getLastModified();
        _sourceLength = _sourcePath.length();
      }

      log.info("Reloading " + cl.getName());

      return false;
    } catch (Exception e) {
      log.log(Level.WARNING, e.toString(), e);
      _classIsModified = true;

      return true;
    }
  }
415321
private static void addBundleFile(final Object bundlefile, final Set<Object> path,
            final ClassLoader classLoader, final ClasspathOrder classpathOrderOut, final ScanSpec scanSpec,
            final LogNode log) {
        // Don't get stuck in infinite loop
        if (bundlefile != null && path.add(bundlefile)) {
            // type File
            final Object basefile = ReflectionUtils.getFieldVal(bundlefile, "basefile", false);
            if (basefile != null) {
                boolean foundClassPathElement = false;
                for (final String fieldName : FIELD_NAMES) {
                    final Object fieldVal = ReflectionUtils.getFieldVal(bundlefile, fieldName, false);
                    foundClassPathElement = fieldVal != null;
                    if (foundClassPathElement) {
                        // We found the base file and a classpath element, e.g. "bin/"
                        classpathOrderOut.addClasspathEntry(basefile.toString() + "/" + fieldVal.toString(),
                                classLoader, scanSpec, log);
                        break;
                    }
                }

                if (!foundClassPathElement) {
                    // No classpath element found, just use basefile
                    classpathOrderOut.addClasspathEntry(basefile.toString(), classLoader, scanSpec, log);
                }

            }
            addBundleFile(ReflectionUtils.getFieldVal(bundlefile, "wrapped", false), path, classLoader,
                    classpathOrderOut, scanSpec, log);
            addBundleFile(ReflectionUtils.getFieldVal(bundlefile, "next", false), path, classLoader,
                    classpathOrderOut, scanSpec, log);
        }
    }
251703
@Override
    protected Class<?> loadClass(String className, boolean resolve)
                    throws ClassNotFoundException {

        Class<?> loadedClass = null;

        synchronized (this) {
            loadedClass = findLoadedClass(className);

            if (loadedClass == null) {
                int index = className.lastIndexOf('.');
                String packageName = index > 0 ? className.substring(0, index) : "";
                if (packageList.contains(packageName)) {
                    try {
                        // first check our classpath
                        loadedClass = findClass(className);
                    } catch (ClassNotFoundException cnfe) {
                        // ignore this since we'll try the parent next
                    }
                }
            }
        }

        if (null == loadedClass) {
            // then the parent classpath
            loadedClass = super.loadClass(className, resolve);
        }

        // The resolve parameter is a legacy parameter that is effectively
        // never used as of JDK 1.1 (see footnote 1 of section 5.3.2 of the 2nd
        // edition of the JVM specification).  The only caller of this method is
        // is java.lang.ClassLoader.loadClass(String), and that method always
        // passes false, so we ignore the parameter.

        return loadedClass;
    }
34890
Class<?> loadClass(String requestedClass, boolean initialize) throws ClassNotFoundException {
    String binaryName = requestedClass;
    while (true) {
      try {
        return Class.forName(binaryName, initialize, classLoader);
      } catch (ClassNotFoundException e) {
        if (!binaryName.contains(".")) {
          throw e;
        } else {
          binaryName = replaceLastPeriodWithDollar(binaryName);
        }
      }
    }
  }
591763
@Override
    public ClassLoader getThrowawayClassLoader() {
        ClassLoader newParent = getThrowawayVersion(getParent());
        ClassLoader[] newFollowOns = new ClassLoader[followOnClassLoaders.size()];
        for (int i = 0; i < newFollowOns.length; i++) {
            newFollowOns[i] = getThrowawayVersion(followOnClassLoaders.get(i));
        }
        return new UnifiedClassLoader(newParent, newFollowOns);
    }
1042630
public static DynamicClassLoader create(ClassLoader parent, PathImpl path)
  {
    DynamicClassLoader loader = new DynamicClassLoader(parent);

    LibraryLoader dirLoader = new LibraryLoader(loader, path);
    dirLoader.init();

    loader.init();
    
    return loader;
  }
507641
public void addClassLoader(final ClassLoader classLoader) {
        if (this.addedClassLoaders == null) {
            this.addedClassLoaders = new ArrayList<>();
        }
        if (classLoader != null) {
            this.addedClassLoaders.add(classLoader);
        }
    }
1254060
private void init() throws IOException {
        if (initialized.compareAndSet(false, true)) {
            log.debug("Init: {}", mode);
            // instance an executor for queue handling
            scheduledExecutorService = Executors.newScheduledThreadPool(schedulerThreadSize, new CustomizableThreadFactory("FileConsumerExecutor-"));
            // if the path is null, the consumer has been uninitialized
            if (path != null) {
                if (log.isDebugEnabled()) {
                    Path parent = path.getParent();
                    log.debug("Parent abs: {} dir: {}", parent.isAbsolute(), Files.isDirectory(parent));
                }
                if (IClientStream.MODE_APPEND.equals(mode)) {
                    if (Files.notExists(path)) {
                        throw new IOException("File to be appended doesnt exist, verify the record mode");
                    }
                    log.debug("Path: {}\nRead: {} write: {} size: {}", path, Files.isReadable(path), Files.isWritable(path), Files.size(path));
                    writer = new FLVWriter(path, true);
                } else if (IClientStream.MODE_RECORD.equals(mode)) {
                    try {
                        // delete existing file
                        if (Files.deleteIfExists(path)) {
                            log.debug("File deleted");
                        }
                        // ensure parent dirs exist
                        Files.createDirectories(path.getParent());
                        // create the file
                        path = Files.createFile(path);
                    } catch (IOException ioe) {
                        log.error("File creation error: {}", ioe);
                    }
                    if (!Files.isWritable(path)) {
                        throw new IOException("File is not writable");
                    }
                    log.debug("Path: {}\nRead: {} write: {}", path, Files.isReadable(path), Files.isWritable(path));
                    writer = new FLVWriter(path, false);
                    if (audioConfigurationTag != null) {
                        writer.writeTag(audioConfigurationTag);
                    }
                    if (videoConfigurationTag != null) {
                        writer.writeTag(videoConfigurationTag);
                        gotVideoKeyframe = true;
                    }
                } else {
                    //throw new IllegalStateException(String.format("Illegal mode type: %s", mode));
                    try {
                        // delete existing file since we're not recording nor appending
                        if (Files.deleteIfExists(path)) {
                            log.debug("File deleted");
                        }
                    } catch (IOException ioe) {
                        log.error("File creation error: {}", ioe);
                    }
                }
            } else {
                log.warn("Consumer is uninitialized");
            }
            log.debug("Init - complete");
        }
    }
1328652
private void handleAuxClassPathFromFile(String filePath) throws IOException {
        try (BufferedReader in = new BufferedReader(UTF8.fileReader(filePath))) {
            while (true) {
                String s = in.readLine();
                if (s == null) {
                    break;
                }
                project.addAuxClasspathEntry(s);
            }
        }
    }
167461
protected void extendRealmClasspath()
            throws MojoExecutionException {
        List<URL> urls = new ArrayList<>();

        for (String fileName : compileClasspathElements) {
            File pathElem = new File(fileName);
            try {
                URL url = pathElem.toURI().toURL();
                urls.add(url);
                getLog().debug("Added classpathElement URL " + url);
            } catch (MalformedURLException e) {
                throw new MojoExecutionException("Error in adding the classpath " + pathElem, e);
            }
        }

        ClassLoader jpaRealm =
                new URLClassLoader(urls.toArray(new URL[urls.size()]), getClass().getClassLoader());

        // set the new ClassLoader as default for this Thread
        // will be reverted in the caller method.
        Thread.currentThread().setContextClassLoader(jpaRealm);
    }
485828
@Override
    public void afterClassLoad(Class<?> clazz) {
        super.afterClassLoad(clazz);
        //force <clinit> to be run, while the correct ThreadLocal is set
        //if we do not run this then <clinit> may be run later, perhaps even in
        //another thread
        try {
            Class.forName(clazz.getName(), true, clazz.getClassLoader());
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
656550
private String buildClassPath()
  {
    String classPath = null;//_classPath;

    if (classPath != null) {
      return classPath;
    }

    if (classPath == null && _loader instanceof DynamicClassLoader) {
      classPath = ((DynamicClassLoader) _loader).getClassPath();
    }
    else { // if (true || _loader instanceof URLClassLoader) {
      StringBuilder sb = new StringBuilder();
      sb.append(CauchoUtil.getClassPath());

      if (_loader != null)
        buildClassPath(sb, _loader);

      classPath = sb.toString();
    }
    //else if (classPath == null)
      //classPath = CauchoSystem.getClassPath();

    String srcDirName = getSourceDirName();
    String classDirName = getClassDirName();

    char sep = CauchoUtil.getPathSeparatorChar();

    if (_extraClassPath != null)
      classPath = classPath + sep + _extraClassPath;

    // Adding the srcDir lets javac and jikes find source files
    if (! srcDirName.equals(classDirName))
      classPath = srcDirName + sep + classPath;
    classPath = classDirName + sep + classPath;

    return classPath;
  }
1109352
@Override
    public DecompilationResult decompileClassFile(Path rootDir, Path classFilePath, Path outputDir)
                throws DecompilationException
    {
        Checks.checkDirectoryToBeRead(rootDir.toFile(), "Classes root dir");
        File classFile = classFilePath.toFile();
        Checks.checkFileToBeRead(classFile, "Class file");
        Checks.checkDirectoryToBeFilled(outputDir.toFile(), "Output directory");

        log.info("Decompiling .class '" + classFilePath + "' to '" + outputDir + "' from: '" + rootDir + "'");

        String name = classFilePath.normalize().toAbsolutePath().toString().substring(rootDir.toAbsolutePath().toString().length() + 1);
        final String typeName = StringUtils.removeEnd(name, ".class");// .replace('/', '.');

        DecompilationResult result = new DecompilationResult();
        try
        {
            DecompilerSettings settings = getDefaultSettings(outputDir.toFile());
            this.procyonConf.setDecompilerSettings(settings); // TODO: This is horrible mess.

            final ITypeLoader typeLoader = new CompositeTypeLoader(new WindupClasspathTypeLoader(rootDir.toString()), new ClasspathTypeLoader());
            WindupMetadataSystem metadataSystem = new WindupMetadataSystem(typeLoader);
            File outputFile = this.decompileType(settings, metadataSystem, typeName);
            result.addDecompiled(Collections.singletonList(classFilePath.toString()), outputFile.getAbsolutePath());
        }
        catch (Throwable e)
        {
            DecompilationFailure failure = new DecompilationFailure("Error during decompilation of "
                        + classFilePath.toString() + ":\n    " + e.getMessage(), Collections.singletonList(name), e);
            log.severe(failure.getMessage());
            result.addFailure(failure);
        }

        return result;
    }
100978
@Override
    public synchronized Class<?> doCompile(String name, String sourceCode, OutputStream os) throws Throwable {

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Begin to compile source code: class is '{}'", name);
        }

        int i = name.lastIndexOf('.');
        String packageName = i < 0 ? "" : name.substring(0, i);
        String className = i < 0 ? name : name.substring(i + 1);
        JavaFileObjectImpl javaFileObject = new JavaFileObjectImpl(className, sourceCode);
        javaFileManager.putFileForInput(StandardLocation.SOURCE_PATH, packageName,
                className + ClassUtils.JAVA_EXTENSION, javaFileObject);

        DiagnosticCollector<JavaFileObject> diagnosticCollector = new DiagnosticCollector<JavaFileObject>();
        Boolean result = compiler.getTask(null, javaFileManager, diagnosticCollector, options, null,
                Arrays.asList(new JavaFileObject[] { javaFileObject })).call();
        if (result == null || !result.booleanValue()) {
            throw new IllegalStateException(
                    "Compilation failed. class: " + name + ", diagnostics: " + diagnosticCollector.getDiagnostics());
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("compile source code done: class is '{}'", name);
            LOGGER.debug("loading class '{}'", name);
        }

        Class<?> retClass = classLoader.loadClass(name);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("loading class done  '{}'", name);
        }

        if (os != null) {
            byte[] bytes = classLoader.loadClassBytes(name);
            if (bytes != null) {
                os.write(bytes);
                os.flush();
            }
        }
        return retClass;
    }
752158
private static Map<Class<?>, SerDeserializer> loadFromClasspath() {
        // log errors to System.err, as problems in static initializers can be troublesome to diagnose
        Map<Class<?>, SerDeserializer> result = new HashMap<>();
        URL url = null;
        try {
            // this is the new location of the file, working on Java 8, Java 9 class path and Java 9 module path
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            if (loader == null) {
                loader = RenameHandler.class.getClassLoader();
            }
            Enumeration<URL> en = loader.getResources("META-INF/org/joda/beans/JodaBeans.ini");
            while (en.hasMoreElements()) {
                url = en.nextElement();
                List<String> lines = loadRenameFile(url);
                parseRenameFile(lines, url, result);
            }
        } catch (Error | Exception ex) {
            System.err.println("ERROR: Unable to load JodaBeans.ini: " + url + ": " + ex.getMessage());
            ex.printStackTrace();
            result.clear();
        }
        return result;
    }