/*
 * Copyright 2013
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at:
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
 * implied. See the License for the specific language governing 
 * permissions and limitations under the License.
 */

package org.openntf.domino.design.impl;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.openntf.domino.design.DatabaseDesign;
import org.openntf.domino.design.XspResource;
import org.openntf.domino.utils.DominoUtils;

/**
 * @author jgallagher
 * 
 */
public class DatabaseClassLoader extends org.openntf.domino.design.DatabaseClassLoader {
	@SuppressWarnings("unused")
	private static final Logger log_ = Logger.getLogger(DatabaseClassLoader.class.getName());

	private final Map<String, byte[]> unloadedClasses_ = new HashMap<String, byte[]>();

	private final boolean includeJars_;
	private boolean loadedJars_ = false;

	private final boolean includeLibraries_;
	private boolean loadedLibraries_ = false;

	private final DatabaseDesign design_;

	public DatabaseClassLoader(final DatabaseDesign design, final ClassLoader parent, final boolean includeJars,
			final boolean includeLibraries) {
		super(parent);
		design_ = design;
		includeJars_ = includeJars;
		includeLibraries_ = includeLibraries;
	}

	@Override
	protected Class<?> findClass(final String name) throws ClassNotFoundException {
		// Check if it's in our pool of in-process classes
		if (unloadedClasses_.containsKey(name)) {
			byte[] classData = unloadedClasses_.remove(name);
			return defineClass(name, classData, 0, classData.length);
		}

		String binaryName = DominoUtils.escapeForFormulaString(DominoUtils.javaBinaryNameToFilePath(name, "/"));

		Iterator<XspResource> classes = design_.getDesignElements(XspResource.class,
				String.format("$ClassIndexItem='WEB-INF/classes/%s' ", binaryName)).iterator();
		if (classes.hasNext()) {
			XspResource res = classes.next();
			// Load up our class queue with the data
			unloadedClasses_.putAll(res.getClassData());
			// Now attempt to load the named class
			byte[] classData = unloadedClasses_.remove(name);
			if (classData != null) {
				// It's possible that an old name of the Java class is still lingering in the NSF
				// In that case, we'd reach this point, but not have an actual class available to load
				return defineClass(name, classData, 0, classData.length);
			}
		}

		// It's also possible that it's stored only as a .class file (e.g. secondary, non-inner classes in a .java)

		Iterator<FileResourceWebContent> webContentFiles = design_.getDesignElements(FileResourceWebContent.class,
				String.format("$FileNames='WEB-INF/classes/%s' ", binaryName)).iterator();
		if (webContentFiles.hasNext()) {
			FileResourceWebContent res = webContentFiles.next();
			byte[] classData = res.getFileData();
			return defineClass(name, classData, 0, classData.length);
		}

		// TODO consider changing the below routines to only loop through resources until found,
		// keeping track of which libraries have been loaded

		// If we're here, see if we should look through the Jars - load them all now
		if (includeJars_ && !loadedJars_) {
			for (org.openntf.domino.design.JarResource jar : design_.getJarResources()) {
				System.out.println(jar.getName());
				unloadedClasses_.putAll(jar.getClassData());
			}
			loadedJars_ = true;

			if (unloadedClasses_.containsKey(name)) {
				byte[] classData = unloadedClasses_.remove(name);
				return defineClass(name, classData, 0, classData.length);
			}
		}

		// Now do the same for Java script libraries
		if (includeLibraries_ && !loadedLibraries_) {
			for (org.openntf.domino.design.ScriptLibraryJava lib : design_.getScriptLibrariesJava()) {
				System.out.println(lib.getName());
				Map<String, byte[]> classData = lib.getClassData();
				unloadedClasses_.putAll(classData);
			}
			loadedLibraries_ = true;

			if (unloadedClasses_.containsKey(name)) {
				byte[] classData = unloadedClasses_.remove(name);
				return defineClass(name, classData, 0, classData.length);
			}
		}

		return super.findClass(name);
	}

	@Override
	public Set<Class<?>> getClassesWithAnnotation(final Class<? extends Annotation> annotationClass) {
		Set<Class<?>> result = new LinkedHashSet<Class<?>>();
		for (String className : design_.getJavaResourceClassNames()) {
			try {
				Class<?> clazz = loadClass(className);
				if (clazz.getAnnotation(annotationClass) != null) {
					result.add(clazz);
				}
			} catch (ClassNotFoundException e) {
				// Ignore - Java resources keep copies of their old names, tripping up the search
			}
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> Set<Class<? extends T>> getClassesExtending(final Class<T> superClass) {
		Set<Class<? extends T>> result = new LinkedHashSet<Class<? extends T>>();
		for (String className : design_.getJavaResourceClassNames()) {
			try {
				Class<?> clazz = loadClass(className);
				if (superClass.isAssignableFrom(clazz)) {
					result.add((Class<? extends T>) clazz);
				}
			} catch (ClassNotFoundException e) {
				// Ignore - Java resources keep copies of their old names, tripping up the search
			}
		}
		return result;
	}

	@Override
	public org.openntf.domino.design.DatabaseDesign getParentDesign() {
		return design_;
	}

}

@@ -14,25 +14,24 @@
 ght 2013%0D%0A *
- 
 %0D%0A * License
@@ -86,17 +86,16 @@
 cense%22);
- 
 %0D%0A * you
@@ -155,17 +155,16 @@
 License.
- 
 %0D%0A * You
@@ -200,25 +200,24 @@
 ense at:%0D%0A *
- 
 %0D%0A * http://
@@ -251,22 +251,20 @@
 ENSE-2.0
- 
 %0D%0A *
- 
 %0D%0A * Unl
@@ -327,17 +327,16 @@
 software
- 
 %0D%0A * dis
@@ -397,17 +397,16 @@
 %22 BASIS,
- 
 %0D%0A * WIT
@@ -465,17 +465,16 @@
 press or
- 
 %0D%0A * imp
@@ -530,17 +530,16 @@
 overning
- 
 %0D%0A * per
@@ -1019,17 +1019,16 @@
 gher%0D%0A *
- 
 %0D%0A */%0D%0Ap
@@ -4289,48 +4289,8 @@
  %7B%0D%0A
-%09%09%09%09System.out.println(lib.getName());%0D%0A
 %09%09%09%09

Remove errant sysout