package org.deri.any23.rdf;

import javax.xml.datatype.XMLGregorianCalendar;

import org.openrdf.model.BNode;
import org.openrdf.model.Literal;
import org.openrdf.model.Resource;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.ValueFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// TODO Move our URI fixing methods to a seperate utility class
public class Any23ValueFactoryWrapper implements ValueFactory{

	private static final Logger logger = LoggerFactory.getLogger(Any23ValueFactoryWrapper.class);
	private final ValueFactory _vFactory;

	public Any23ValueFactoryWrapper(final ValueFactory vFactory) {
		_vFactory = vFactory;

	}

	@Override
	public BNode createBNode() {
		return _vFactory.createBNode();
	}

	@Override
	public BNode createBNode(String arg0) {
		if (arg0 == null) return null;
		return _vFactory.createBNode(arg0);
	}

	@Override
	public Literal createLiteral(String arg0) {
		if (arg0 == null) return null;
		return _vFactory.createLiteral(arg0);
	}

	@Override
	public Literal createLiteral(boolean arg0) {
		return _vFactory.createLiteral(arg0);
	}

	@Override
	public Literal createLiteral(byte arg0) {
		return _vFactory.createLiteral(arg0);
	}

	@Override
	public Literal createLiteral(short arg0) {
		return _vFactory.createLiteral(arg0);
	}

	@Override
	public Literal createLiteral(int arg0) {
		return _vFactory.createLiteral(arg0);
	}

	@Override
	public Literal createLiteral(long arg0) {
		return _vFactory.createLiteral(arg0);
	}

	@Override
	public Literal createLiteral(float arg0) {
		return _vFactory.createLiteral(arg0);
	}

	@Override
	public Literal createLiteral(double arg0) {
		return _vFactory.createLiteral(arg0);
	}

	@Override
	public Literal createLiteral(XMLGregorianCalendar arg0) {
		return _vFactory.createLiteral(arg0);
	}

	@Override
	public Literal createLiteral(String arg0, String arg1) {
		if (arg0 == null) return null;
		return _vFactory.createLiteral(arg0,arg1);
	}

	@Override
	public Literal createLiteral(String arg0, URI arg1) {
		if (arg0 == null) return null;
		return _vFactory.createLiteral(arg0,arg1);
	}

	@Override
	public Statement createStatement(Resource arg0, URI arg1, Value arg2) {
		if (arg0 == null || arg1 == null || arg2 == null) return null;
		return _vFactory.createStatement(arg0,arg1,arg2);
	}

	@Override
	public Statement createStatement(Resource arg0, URI arg1, Value arg2,
			Resource arg3) {
		if (arg0 == null || arg1 == null || arg2 == null) return null;
		return _vFactory.createStatement(arg0,arg1,arg2,arg3);
	}

	@Override
	/**
	 * @param arg0
	 * @return a valid sesame URI or null if any exception occured
	 */
	public URI createURI(String arg0) {
		if (arg0 == null) return null;
		return _vFactory.createURI(fixURIWithException(arg0));
	}
	
	/**
	 * 
	 * @return a valid sesame URI or null if any exception occured
	 */
	public URI createURI(String arg0, String arg1) {
		if (arg0 == null || arg1 == null) return null;
		return _vFactory.createURI(fixURIWithException(arg0),arg1);
	}

	/**
	 * Fixes typical errors in URIs, and resolves relative URIs against a base URI.
	 * @param uri A URI, relative or absolute, can have typical syntax errors
	 * @param baseURI A base URI to use for resolving relative URIs
	 * @return An absolute URI, sytnactically valid, or null if not fixable
	 */
	public URI resolveURI(String uri, java.net.URI baseURI) {
		try {
			return _vFactory.createURI(baseURI.resolve(fixURIWithException(uri)).toString());
		} catch (IllegalArgumentException ex) {
			logger.warn(ex.getMessage());
			return null;
		}
	}

	/**
	 * @param uri
	 * @return a valid sesame URI or null if any exception occured
	 */
	public URI fixURI(String uri) {
		try {
			return _vFactory.createURI(fixURIWithException(uri));
		} catch (Exception e){
			logger.warn(e.getMessage());
			return null;
		}
	}
	

	/**
	 * Fixes typical errors in an absolute URI, such as unescaped spaces.
	 * @param uri An absolute URI, can have typical syntax errors
	 * @return An absolute URI that is valid against the URI syntax
	 * @throws IllegalArgumentException if URI is not fixable
	 */
	public static String fixAbsoluteURI(String uri) {
		String fixed = fixURIWithException(uri);
		if (!fixed.matches("[a-zA-Z0-9]+://.*")) throw new IllegalArgumentException("not a absolute URI: " + uri);
		return fixed;
	}

	/**
	 * Tries to fix a potentially broken relative or absolute URI 
	 * 
	 *These appear to be good rules:
	 *
	 *		Remove whitespace or '\' or '"' in beginning and end
	 *		Replace space with %20
	 *		Drop the triple if it matches this regex (only protocol): ^[a-zA-Z0-9]+:(//)?$
	 *		Drop the triple if it matches this regex: ^javascript:
	 *		Truncate ">.*$ from end of lines (Neko didn't quite manage to fix broken markup)
	 *		Drop the triple if any of these appear in the URL: <>[]|*{}"<>\
	 */
	private static String fixURIWithException(String unescapedURI) {
		//	Remove starting and ending whitespace  
		String escapedURI = unescapedURI.trim();
		
		//Replace space with %20
		escapedURI = escapedURI.replaceAll(" ", "%20");
		
		//strip linebreaks 
		escapedURI = escapedURI.replaceAll("\n", "");
		
		//'Remove starting  "\" or '"'  
		if(escapedURI.startsWith("\\") || escapedURI.startsWith("\"")) escapedURI = escapedURI.substring(1);
		//Remove  ending   "\" or '"'
		if(escapedURI.endsWith("\\") || escapedURI.endsWith("\"")) escapedURI = escapedURI.substring(0,escapedURI.length()-1);
		
		//Drop the triple if it matches this regex (only protocol): ^[a-zA-Z0-9]+:(//)?$
		if(escapedURI.matches("^[a-zA-Z0-9]+:(//)?$")) throw new IllegalArgumentException("no authority in URI: " + unescapedURI);
		
		//Drop the triple if it matches this regex: ^javascript:
		if(escapedURI.matches("^javascript:")) throw new IllegalArgumentException("URI starts with javascript: " + unescapedURI);
				
//		//stripHTML
//		escapedURI = escapedURI.replaceAll("\\<.*?\\>", "");
		
		//>.*$ from end of lines (Neko didn't quite manage to fix broken markup)
		escapedURI = escapedURI.replaceAll(">.*$", "");
		
		//Drop the triple if any of these appear in the URL: <>[]|*{}"<>\
		// TODO write a test for this
		if(escapedURI.matches("[<>\\[\\]|\\*\\{\\}\"\\\\]")) throw new IllegalArgumentException("Invalid character in URI: " + unescapedURI);
		
		return escapedURI;
	}
}

@@ -4961,16 +4961,99 @@
 dURI) %7B%0A
+%09%09if (unescapedURI == null) throw new IllegalArgumentException(%22URI was null%22);%0A%09%09%0A
 %09%09//%09Rem

Fix NPE in Any23ValueFactoryWrapper