/* **********************************************************************
 * source:    MySchemaHelper
 * package:   com.me.my.xml.toys
 * author:    sisjo
 ************************************************************************/
package com.me.my.xml.toys;

import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Schema;
import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.Source;
import java.util.*;
import java.io.File;
import java.io.InputStream;

/* ************
* class: MySchemaHelper
* <p>
* This is a class to provide schema and encoding help
*/
public final class MySchemaHelper {
  // encoding helper
  static public  final String         Key_Utf_8     = "UTF8";
  static public  final String         Key_Iso_8859  = "ISO8859";
  static public  final String         Enc_Utf_8     = "UTF-8";
  static public  final String         Enc_Iso_8859  = "ISO-8859-1";

  static private final MySchemaHelper myInst = new MySchemaHelper();

  /*******
   * accessor
   * @return
   */
  static public MySchemaHelper getInstance() {
    return myInst;
  }

  /*******
   * validity
   * @return
   */
  static public boolean isValid() {
    return myInst.isValid;
  }

  // ===
  private boolean                 isValid = false;
  private Map<String,String>      myCS;
  private SchemaFactory           mySF;
  private Map<String,Schema>      mySchemas;

  /*******
   * ctor
   */
  private MySchemaHelper() {
    boolean ok = true;
    // encodings
    TreeMap<String,String> cs = new TreeMap<String,String>();
    cs.put(Key_Utf_8, Enc_Utf_8);
    cs.put(Key_Iso_8859, Enc_Iso_8859);
    // factory
    SchemaFactory  sf = null;
    if (ok) try {
      sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    } catch(Throwable th) {
      sf = null;
      ok = false;
    } finally {
      myCS = cs;
      mySF = sf;
      mySchemas = new TreeMap<String,Schema>();
      isValid = ok;
    }
  }

  /*************
   * get encoding by key
   * @param key
   * @return
   */
  protected String getEncodingCS(String key) {
    String ec = myCS.get(key);
    return ec;
  }

  /*************
   * get an XML encoding from a key
   *
   * @param encodingKey
   * @return
   */
  public String getEncoding(String encodingKey) {
    return MyMarshallingBase.badStr(getEncoding(encodingKey), Enc_Utf_8);
  }

  /*************
   * get an xml encoding from a property set
   *
   * @param props
   * @param propKey
   * @return
   */
  public String getEncoding(Properties props, String propKey) {
    String key = ((props != null) ? props.getProperty(MyMarshallingBase.badStr(propKey, "?"), "?") : "?");
    return getEncoding(key);
  }

  /*************
   * add a schema
   * @param schemaKey
   * @param schema
   * @return
   * @throws Exception
   */
  public boolean addSchema(String schemaKey, File schema) throws Exception {
    boolean ok = ((mySF != null) && MyMarshallingBase.okStr(schemaKey, true) && MyMarshallingBase.okFile(schema));
    if (ok && !mySchemas.containsKey(schemaKey.toUpperCase())) synchronized(myCS) {
      Schema sm = mySF.newSchema(schema);
      mySchemas.put(schemaKey.toUpperCase(), sm);
    }
    return ok;
  }

  /*************
   * add a schema
   * @param schemaKey
   * @param schema
   * @return
   * @throws Exception
   */
  public boolean addSchema(String schemaKey, InputStream schema) throws Exception {
    boolean ok = ((mySF != null) && MyMarshallingBase.okStr(schemaKey, true) && (schema != null));
    if (ok && !mySchemas.containsKey(schemaKey.toUpperCase())) synchronized(myCS) {
      StreamSource ss = new StreamSource(schema);
      Schema sm = mySF.newSchema(ss);
      mySchemas.put(schemaKey.toUpperCase(), sm);
    }
    return ok;
  }

  /*************
   * add a schema
   * @param schemaKey
   * @param schema
   * @return
   * @throws Exception
   */
  public boolean addSchema(String schemaKey, Source schema) throws Exception {
    boolean ok = ((mySF != null) && MyMarshallingBase.okStr(schemaKey, true) && (schema != null));
    if (ok && !mySchemas.containsKey(schemaKey.toUpperCase())) synchronized(myCS) {
      Schema sm = mySF.newSchema(schema);
      mySchemas.put(schemaKey.toUpperCase(), sm);
    }
    return ok;
  }

  /*************
   * add a schema
   * @param schemaKey
   * @param schemas
   * @return
   * @throws Exception
   */
  public boolean addSchema(String schemaKey, File[] schemas) throws Exception {
    boolean ok = ((mySF != null) && MyMarshallingBase.okStr(schemaKey, true) && (schemas != null));
    if (ok && !mySchemas.containsKey(schemaKey.toUpperCase())) synchronized(myCS) {
      List<Source> ls = new ArrayList<Source>();
      for (int iS=0; iS < schemas.length; iS++) {
        if (MyMarshallingBase.okFile(schemas[iS])) {
          ls.add(new StreamSource(schemas[iS]));
        }
      }
      Source[] ss = ls.toArray(new Source[0]);
      Schema sm = mySF.newSchema(ss);
      mySchemas.put(schemaKey.toUpperCase(), sm);
    }
    return ok;
  }

  /*************
   * add a schema
   * @param schemaKey
   * @param schemas
   * @return
   * @throws Exception
   */
  public boolean addSchema(String schemaKey, InputStream[] schemas) throws Exception {
    boolean ok = ((mySF != null) && MyMarshallingBase.okStr(schemaKey, true) && (schemas != null));
    if (ok && !mySchemas.containsKey(schemaKey.toUpperCase())) synchronized(myCS) {
      Source[] ss = new Source[schemas.length];
      for (int iS=0; iS < schemas.length; iS++) {
        ss[iS] = new StreamSource(schemas[iS]);
      }
      Schema sm = mySF.newSchema(ss);
      mySchemas.put(schemaKey.toUpperCase(), sm);
    }
    return ok;
  }

  /*************
   * add a schema
   * @param schemaKey
   * @param schemas
   * @return
   * @throws Exception
   */
  public boolean addSchema(String schemaKey, List<InputStream> schemas) throws Exception {
    boolean ok = ((mySF != null) && MyMarshallingBase.okStr(schemaKey, true) && (schemas != null));
    if (ok && !mySchemas.containsKey(schemaKey.toUpperCase())) synchronized(myCS) {
      Source[] ss = new Source[schemas.size()];
      int iS=0;
      for (Iterator<InputStream> it=schemas.iterator(); it.hasNext();) {
        ss[iS++] = new StreamSource(it.next());
      }
      Schema sm = mySF.newSchema(ss);
      mySchemas.put(schemaKey.toUpperCase(), sm);
    }
    return ok;
  }

  /*************
   * add a schema
   * @param schemaKey
   * @param schemas
   * @return
   * @throws Exception
   */
  public boolean addSchema(String schemaKey, Source[] schemas) throws Exception {
    boolean ok = ((mySF != null) && MyMarshallingBase.okStr(schemaKey, true) && (schemas != null));
    if (ok && !mySchemas.containsKey(schemaKey.toUpperCase())) synchronized(myCS) {
      Schema sm = mySF.newSchema(schemas);
      mySchemas.put(schemaKey.toUpperCase(), sm);
    }
    return ok;
  }

  /*************
   * get a schema
   * @param schemaKey
   * @return
   */
  public Schema getSchema(String schemaKey) {
    boolean ok = ((mySF != null) && MyMarshallingBase.okStr(schemaKey, true));
    if (ok && mySchemas.containsKey(schemaKey.toUpperCase())) {
      return mySchemas.get(schemaKey.toUpperCase());
    }
    return null;
  }

}
