package org.strategoxt.lang;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.spoofax.interpreter.terms.IStrategoTerm;
import org.spoofax.interpreter.terms.ITermFactory;
import org.spoofax.terms.LazyTerm;
import org.spoofax.terms.io.binary.TermReader;

/**
 * @author Lennart Kats <lennart add lclnet.nl>
 */
public class ImportTerm extends LazyTerm {
	
	private final ITermFactory factory;
	
	private final String path, name;
	
	private final Class<?> container;
	
	private ZipFile lastZipFile;
	
	public ImportTerm(ITermFactory factory, Class<?> container, String path, String name) {
		assert path.endsWith("/");
		this.factory = factory;
		this.container = container;
		this.path = path;
		this.name = name;
	}
	
	@Override
	protected IStrategoTerm init() {
		InputStream stream = openStream();
		try {
			IStrategoTerm result = new TermReader(factory).parseFromStream(stream);
			return result;
		} catch (java.io.IOException e) {
			throw new StrategoException(container.getSimpleName()
					+ ": Could not read imported term file " + name, e);
		} catch (RuntimeException e) {
			throw new StrategoException(container.getSimpleName()
					+ ": Could not read imported term file " + name, e);
		} finally {
			try {
				if (stream != null)
					stream.close();
				if (lastZipFile != null)
					lastZipFile.close();
				lastZipFile = null;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private InputStream openStream() {
		InputStream result = tryOpenStreamFromJar();
		if (result == null)
			result = container.getResourceAsStream(removePrecedingSlash(path) + name);
		if (result == null)
			result = container.getResourceAsStream(path + name);
		if (result == null)
			result = container.getResourceAsStream("/" + name);
		if (result == null)
			throw new StrategoException(container.getSimpleName()
					+ ": Could not find imported term file " + name);
		return result;
	}

	/**
	 * Attempt to open resources in JARs using the Java ZipFile API,
	 * hopefully avoiding JVM classloader crashes.
	 */
	private InputStream tryOpenStreamFromJar() {
		try {
			URL location = container.getProtectionDomain().getCodeSource().getLocation();
			if (location.getFile().endsWith(".jar")) {
				File jarFile = new File(location.toURI());
				ZipFile jar = lastZipFile = new ZipFile(jarFile);
				ZipEntry entry = jar.getEntry(removePrecedingSlash(path) + name);
				if (entry == null) entry = jar.getEntry(path + name);
				if (entry == null) entry = jar.getEntry(name);
				if (entry == null) return null;
				return jar.getInputStream(entry);
			} else {
				return null;
			}
		} catch (Exception e) {
			return null; // be forgiving
		}
	}
	
	private static String removePrecedingSlash(String path) {
		return path.startsWith("/") ? path.substring(1) : path;
	}
}

@@ -485,16 +485,26 @@
 e final 
+transient 
 ITermFac
@@ -534,16 +534,26 @@
 e final 
+transient 
 String p
@@ -580,16 +580,26 @@
 e final 
+transient 
 Class%3C?%3E
@@ -621,16 +621,26 @@
 private 
+transient 
 ZipFile 
@@ -2942,10 +2942,250 @@
 ath;%0A%09%7D%0A
+  %0A  private void writeObject(java.io.ObjectOutputStream out)%0A      throws IOException %7B%0A    // force the lazy term before serialization so we don't need the fields in this class any more%0A    getWrapped();%0A    out.defaultWriteObject();%0A  %7D%0A
 %7D%0A

Make ImportTerm Serializable