/* Copyright (c) 2008 Charles Rich and Worcester Polytechnic Institute.
 * All Rights Reserved.  Use is subject to license terms.  See the file
 * "license.terms" for information on usage and redistribution of this
 * file and for a DISCLAIMER OF ALL WARRANTIES.
 */
package edu.wpi.cetask;

import com.thaiopensource.datatype.xsd.DatatypeLibraryFactoryImpl;
import com.thaiopensource.util.PropertyMapBuilder;
import com.thaiopensource.validate.ValidationDriver;
import com.thaiopensource.validate.rng.*;
import org.w3c.dom.*;

import org.xml.sax.InputSource;

import java.io.*;
import java.net.*;

public abstract class Utils {

   private Utils () {}  // non-instantiatable class for static methods only

   /**
    * Validate given task model description against CEA-2018 RelaxNG schema
    * (http://ce.org/cea-2018/rnc). Validation can also be run stand-alone at command
    * line as in:<br>
    * <br>
    * <tt>java -jar lib/jing.jar -c java/edu/wpi/cetask/CEA-2018.rnc MyModel.xml</tt>
    * 
    * @param from string identifying url, system resource or file to validate
    * @return true iff model is valid
    */
   public static boolean validate (String from) {
      // explicit datatype factory is workaround for Jing bug wrt to OS X
      PropertyMapBuilder properties = new PropertyMapBuilder();
      RngProperty.DATATYPE_LIBRARY_FACTORY.put(properties,
            new DatatypeLibraryFactoryImpl());
      ValidationDriver driver = 
         new ValidationDriver(properties.toPropertyMap(),
               CompactSchemaReader.getInstance());
      try {
         driver.loadSchema(
               new InputSource(Utils.class.getResourceAsStream("cea-2018.rnc")));
         boolean valid = driver.validate(new InputSource(toURL(from).openStream()));
         if ( !valid ) System.err.println("Validation errors above in "+from);
         return valid;
      } catch (Exception e) { 
         System.out.println(e);
         return false;
      }
   }
   
   // three-valued logic (null represents unknown)
   
   public static boolean isTrue (Boolean b) {
      return b != null && b;
   }
   
   public static boolean isFalse (Boolean b) {
      return b != null && !b;
   }

   /**
    * Test whether given objects are either both null or
    * {@link Object#equals(Object)}. 
    */
   public static boolean equals (Object object1, Object object2) {
      return object1 == null ? object2 == null : object1.equals(object2);
   }
   
   /**
    * Return null if given string is empty, otherwise string.  
    */
   public static String emptyNull (String string) {
      return string.length() == 0 ? null : string;
   }
   
   /**
    * Convert the given source string to a URL.  If given string is not a well-formed
    * URL, then see if it names a system resource; otherwise treat it as a filename.
    */
   public static URL toURL (String source) throws MalformedURLException {
      try { return new URL(source); }
      catch (MalformedURLException e) {
         URL url = ClassLoader.getSystemResource(source);
         // sic toURI() first (see usage note in File.toURL)
         return url == null ? new File(source).toURI().toURL() : url;
      }
   }
   
   /**
    * If given string ends with 'from' string, replace ending with 'to' string;
    * otherwise just append 'to' string.
    */
   public static String replaceEndsWith (String string, String from, String to) {
      return ( string.endsWith(from) ?
         string.substring(0, string.length()-from.length()) : string) 
         + to;
   }
   
   /**
    * Return (possibly copy of) given string with first character lower case.
    */
   public static String decapitalize (String string) {
      if ( Character.isLowerCase(string.charAt(0)) ) return string;
      char chars[] = string.toCharArray();
      chars[0] = Character.toLowerCase(chars[0]);
      return new String(chars);
   }
   
   /**
    * Return (possibly copy of) given string with first character upper case.
    */
   public static String capitalize (String string) {
      if ( Character.isUpperCase(string.charAt(0)) ) return string;
      char chars[] = string.toCharArray();
      chars[0] = Character.toUpperCase(chars[0]);
      return new String(chars);
   }
   
   /**
    * Strip first arg from main args
    */
   public static String[] restArgs (String[] args) {
      String[] rest = new String[args.length-1];
      if ( rest.length > 0 ) System.arraycopy(args, 1, rest, 0, rest.length);
      return rest;
   }
   
   /**
    * TODO Limited temporary fix for bugging version in JDK 1.6
    * of com.sun.org.apache.xerces.internal.dom.NodeImpl.lookupNamespaceURI(String)
    */
   public static String lookupNamespaceURI (Node node, String prefix) {
      // assume namespace definitions at document root
      NamedNodeMap map =node.getOwnerDocument().getDocumentElement().getAttributes();
      for (int i = map.getLength(); i-- > 0;) {
         Node attribute = map.item(i);
         String name = attribute.getNodeName();
         int colon = name.indexOf(':');
         if ( colon >= 0 && name.substring(0, colon).equals("xmlns") 
               && name.substring(colon+1).equals(prefix) )
            return attribute.getNodeValue();
         }
      return null;
   }
}
